Updated on 2026-08-14
This commit is contained in:
parent
46a28ab22c
commit
c83081cc11
28 changed files with 411 additions and 153 deletions
|
|
@ -33,7 +33,6 @@ internal class VisaCardScanHandler @Inject constructor(
|
|||
|
||||
private class SessionContext(
|
||||
val visaActivationRepository: VisaActivationRepository,
|
||||
val cardId: String,
|
||||
val session: CardSession,
|
||||
)
|
||||
|
||||
|
|
@ -54,7 +53,6 @@ internal class VisaCardScanHandler @Inject constructor(
|
|||
|
||||
val context = SessionContext(
|
||||
visaActivationRepository = visaActivationRepository,
|
||||
cardId = card.cardId,
|
||||
session = session,
|
||||
)
|
||||
|
||||
|
|
@ -140,7 +138,9 @@ internal class VisaCardScanHandler @Inject constructor(
|
|||
Timber.i(
|
||||
"Failed to get Access token for Wallet public key authoziation. Authorizing using Card Pub key",
|
||||
)
|
||||
return handleCardAuthorization()
|
||||
return handleCardAuthorization(
|
||||
cardWalletAddress = walletAddress.value,
|
||||
)
|
||||
}
|
||||
|
||||
val signChallengeResult = signChallengeWithWallet(
|
||||
|
|
@ -153,6 +153,7 @@ internal class VisaCardScanHandler @Inject constructor(
|
|||
is CompletionResult.Success -> {
|
||||
Timber.i("Challenge signed with Wallet public key")
|
||||
handleWalletAuthorizationTokens(
|
||||
cardWalletAddress = walletAddress.value,
|
||||
signedChallenge = challengeResponse
|
||||
.toSignedChallenge(signChallengeResult.data.signature.toHexString()),
|
||||
)
|
||||
|
|
@ -165,6 +166,7 @@ internal class VisaCardScanHandler @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun SessionContext.handleWalletAuthorizationTokens(
|
||||
cardWalletAddress: String,
|
||||
signedChallenge: VisaAuthSignedChallenge,
|
||||
): CompletionResult<VisaCardActivationStatus> {
|
||||
val authorizationTokensResponse = runCatching {
|
||||
|
|
@ -173,7 +175,9 @@ internal class VisaCardScanHandler @Inject constructor(
|
|||
Timber.i(
|
||||
"Failed to get Access token for Wallet public key authoziation. Authorizing using Card Pub key",
|
||||
)
|
||||
return handleCardAuthorization()
|
||||
return handleCardAuthorization(
|
||||
cardWalletAddress = cardWalletAddress,
|
||||
)
|
||||
}
|
||||
|
||||
Timber.i("Authorized using Wallet public key successfully")
|
||||
|
|
@ -181,7 +185,9 @@ internal class VisaCardScanHandler @Inject constructor(
|
|||
return CompletionResult.Success(VisaCardActivationStatus.Activated(authorizationTokensResponse))
|
||||
}
|
||||
|
||||
private suspend fun SessionContext.handleCardAuthorization(): CompletionResult<VisaCardActivationStatus> {
|
||||
private suspend fun SessionContext.handleCardAuthorization(
|
||||
cardWalletAddress: String,
|
||||
): CompletionResult<VisaCardActivationStatus> {
|
||||
val card = session.environment.card ?: return CompletionResult.Failure(TangemSdkError.MissingPreflightRead())
|
||||
|
||||
Timber.i("Requesting authorization challenge to sign")
|
||||
|
|
@ -258,6 +264,7 @@ internal class VisaCardScanHandler @Inject constructor(
|
|||
activationInput = activationInput,
|
||||
authTokens = authorizationTokensResponse,
|
||||
remoteState = activationRemoteState,
|
||||
cardWalletAddress = cardWalletAddress,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class MockVisaActivationRepository @AssistedInject constructor(
|
|||
}
|
||||
|
||||
override suspend fun getActivationRemoteStateLongPoll(): VisaActivationRemoteState {
|
||||
return VisaActivationRemoteState.WaitingPinCode
|
||||
return VisaActivationRemoteState.PaymentAccountDeploying
|
||||
}
|
||||
|
||||
override suspend fun getCardWalletAcceptanceData(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.domain.visa.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class VisaActivationOrderInfo(
|
||||
@Json(name = "orderId") val orderId: String,
|
||||
@Json(name = "customer_id") val customerId: String,
|
||||
@Json(name = "customer_wallet_address") val customerWalletAddress: String,
|
||||
)
|
||||
|
|
@ -13,14 +13,16 @@ sealed class VisaActivationRemoteState {
|
|||
|
||||
@Serializable
|
||||
data class CustomerWalletSignatureRequired(
|
||||
val request: VisaCardWalletDataToSignRequest,
|
||||
val activationOrderInfo: VisaActivationOrderInfo,
|
||||
) : VisaActivationRemoteState()
|
||||
|
||||
@Serializable
|
||||
data object PaymentAccountDeploying : VisaActivationRemoteState()
|
||||
|
||||
@Serializable
|
||||
data object WaitingPinCode : VisaActivationRemoteState()
|
||||
data class WaitingPinCode(
|
||||
val activationOrderInfo: VisaActivationOrderInfo,
|
||||
) : VisaActivationRemoteState()
|
||||
|
||||
@Serializable
|
||||
data object WaitingForActivationFinishing : VisaActivationRemoteState()
|
||||
|
|
@ -39,7 +41,8 @@ sealed class VisaActivationRemoteState {
|
|||
@Suppress("ClassNaming")
|
||||
class VisaActivationRemoteState_Json(
|
||||
@Json(name = "type") val type: VisaActivationRemoteState_Type,
|
||||
@Json(name = "request") val request: VisaCardWalletDataToSignRequest? = null,
|
||||
@Json(name = "requestCardWallet") val requestCardWallet: VisaCardWalletDataToSignRequest? = null,
|
||||
@Json(name = "activationOrderInfo") val activationOrderInfo: VisaActivationOrderInfo? = null,
|
||||
)
|
||||
|
||||
@Suppress("ClassNaming")
|
||||
|
|
@ -62,11 +65,12 @@ class VisaActivationRemoteState_JsonAdapter {
|
|||
fun fromJson(value: VisaActivationRemoteState_Json): VisaActivationRemoteState {
|
||||
return when (value.type) {
|
||||
VisaActivationRemoteState_Type.CardWalletSignatureRequired ->
|
||||
VisaActivationRemoteState.CardWalletSignatureRequired(value.request!!)
|
||||
VisaActivationRemoteState.CardWalletSignatureRequired(value.requestCardWallet!!)
|
||||
VisaActivationRemoteState_Type.CustomerWalletSignatureRequired ->
|
||||
VisaActivationRemoteState.CustomerWalletSignatureRequired(value.request!!)
|
||||
VisaActivationRemoteState.CustomerWalletSignatureRequired(value.activationOrderInfo!!)
|
||||
VisaActivationRemoteState_Type.PaymentAccountDeploying -> VisaActivationRemoteState.PaymentAccountDeploying
|
||||
VisaActivationRemoteState_Type.WaitingPinCode -> VisaActivationRemoteState.WaitingPinCode
|
||||
VisaActivationRemoteState_Type.WaitingPinCode ->
|
||||
VisaActivationRemoteState.WaitingPinCode(value.activationOrderInfo!!)
|
||||
VisaActivationRemoteState_Type.WaitingForActivationFinishing ->
|
||||
VisaActivationRemoteState.WaitingForActivationFinishing
|
||||
VisaActivationRemoteState_Type.Activated -> VisaActivationRemoteState.Activated
|
||||
|
|
@ -80,17 +84,20 @@ class VisaActivationRemoteState_JsonAdapter {
|
|||
is VisaActivationRemoteState.CardWalletSignatureRequired ->
|
||||
VisaActivationRemoteState_Json(
|
||||
type = VisaActivationRemoteState_Type.CardWalletSignatureRequired,
|
||||
request = value.request,
|
||||
requestCardWallet = value.request,
|
||||
)
|
||||
is VisaActivationRemoteState.CustomerWalletSignatureRequired ->
|
||||
VisaActivationRemoteState_Json(
|
||||
type = VisaActivationRemoteState_Type.CustomerWalletSignatureRequired,
|
||||
request = value.request,
|
||||
activationOrderInfo = value.activationOrderInfo,
|
||||
)
|
||||
is VisaActivationRemoteState.PaymentAccountDeploying ->
|
||||
VisaActivationRemoteState_Json(VisaActivationRemoteState_Type.PaymentAccountDeploying)
|
||||
is VisaActivationRemoteState.WaitingPinCode ->
|
||||
VisaActivationRemoteState_Json(VisaActivationRemoteState_Type.WaitingPinCode)
|
||||
VisaActivationRemoteState_Json(
|
||||
VisaActivationRemoteState_Type.WaitingPinCode,
|
||||
activationOrderInfo = value.activationOrderInfo,
|
||||
)
|
||||
is VisaActivationRemoteState.WaitingForActivationFinishing ->
|
||||
VisaActivationRemoteState_Json(VisaActivationRemoteState_Type.WaitingForActivationFinishing)
|
||||
is VisaActivationRemoteState.Activated -> VisaActivationRemoteState_Json(
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ sealed class VisaCardActivationStatus {
|
|||
val activationInput: VisaActivationInput,
|
||||
val authTokens: VisaAuthTokens,
|
||||
val remoteState: VisaActivationRemoteState,
|
||||
val cardWalletAddress: String,
|
||||
) : VisaCardActivationStatus()
|
||||
|
||||
@Serializable
|
||||
|
|
@ -43,6 +44,7 @@ class VisaCardActivationStatus_Json(
|
|||
@Json(name = "activationInput") val activationInput: VisaActivationInput? = null,
|
||||
@Json(name = "authTokens") val authTokens: VisaAuthTokens? = null,
|
||||
@Json(name = "remoteState") val remoteState: VisaActivationRemoteState? = null,
|
||||
@Json(name = "cardWalletAddress") val cardWalletAddress: String? = null,
|
||||
)
|
||||
|
||||
// do not rename instances
|
||||
|
|
@ -67,6 +69,7 @@ class VisaCardActivationStatus_JsonAdapter {
|
|||
value.activationInput!!,
|
||||
value.authTokens!!,
|
||||
value.remoteState!!,
|
||||
value.cardWalletAddress!!,
|
||||
)
|
||||
VisaCardActivationStatus_Type.NotStartedActivation -> VisaCardActivationStatus.NotStartedActivation(
|
||||
value.activationInput!!,
|
||||
|
|
@ -88,6 +91,7 @@ class VisaCardActivationStatus_JsonAdapter {
|
|||
value.activationInput,
|
||||
value.authTokens,
|
||||
value.remoteState,
|
||||
value.cardWalletAddress,
|
||||
)
|
||||
is VisaCardActivationStatus.NotStartedActivation -> VisaCardActivationStatus_Json(
|
||||
VisaCardActivationStatus_Type.NotStartedActivation,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,3 @@
|
|||
package com.tangem.domain.visa.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class VisaCardWalletDataToSignRequest(
|
||||
@Json(name = "orderId") val orderId: String,
|
||||
@Json(name = "customer_id") val customerId: String,
|
||||
@Json(name = "customer_wallet_address") val customerWalletAddress: String,
|
||||
)
|
||||
typealias VisaCardWalletDataToSignRequest = VisaActivationOrderInfo
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
package com.tangem.domain.visa.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class VisaDataToSignByCardWallet(
|
||||
val request: VisaCardWalletDataToSignRequest,
|
||||
val hashToSign: String,
|
||||
|
|
|
|||
|
|
@ -96,17 +96,36 @@ internal class DefaultOnboardingVisaComponent @AssistedInject constructor(
|
|||
return when (route) {
|
||||
is OnboardingVisaRoute.Welcome -> OnboardingVisaWelcomeComponent(
|
||||
appComponentContext = factoryContext,
|
||||
config = OnboardingVisaWelcomeComponent.Config.Welcome,
|
||||
params = OnboardingVisaWelcomeComponent.Params(
|
||||
isWelcomeBack = route.isWelcomeBack,
|
||||
childParams = childParams,
|
||||
onDone = { model.navigateFromWelcome(route) },
|
||||
onDone = { model.stackNavigation.push(OnboardingVisaRoute.AccessCode) },
|
||||
),
|
||||
)
|
||||
is OnboardingVisaRoute.WelcomeBack -> OnboardingVisaWelcomeComponent(
|
||||
appComponentContext = factoryContext,
|
||||
config = OnboardingVisaWelcomeComponent.Config.WelcomeBack(
|
||||
activationInput = route.activationInput,
|
||||
dataToSignRequest = route.dataToSignByCardWalletRequest,
|
||||
scanResponse = model.currentScanResponse.value,
|
||||
),
|
||||
params = OnboardingVisaWelcomeComponent.Params(
|
||||
childParams = childParams,
|
||||
onDone = {
|
||||
val activationReadyEvent =
|
||||
(it as? OnboardingVisaWelcomeComponent.DoneEvent.WelcomeBackDone)?.activationReadyEvent
|
||||
|
||||
if (activationReadyEvent != null) {
|
||||
model.navigateFromActivationScreen(activationReadyEvent)
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
OnboardingVisaRoute.AccessCode -> OnboardingVisaAccessCodeComponent(
|
||||
appComponentContext = factoryContext,
|
||||
params = OnboardingVisaAccessCodeComponent.Params(
|
||||
childParams = childParams,
|
||||
onDone = { model.navigateFromAccessCode(it) },
|
||||
onDone = { model.navigateFromActivationScreen(it) },
|
||||
),
|
||||
config = OnboardingVisaAccessCodeComponent.Config(
|
||||
scanResponse = model.currentScanResponse.value,
|
||||
|
|
@ -129,7 +148,7 @@ internal class DefaultOnboardingVisaComponent @AssistedInject constructor(
|
|||
onDone = {
|
||||
model.stackNavigation.push(
|
||||
OnboardingVisaRoute.PinCode(
|
||||
activationOrderId = "TODO", // TODO [REDACTED_TASK_KEY] [Visa] In progress screen after pin code
|
||||
activationOrderInfo = TODO(), // TODO [REDACTED_TASK_KEY] [Visa] In progress screen after pin code
|
||||
),
|
||||
)
|
||||
},
|
||||
|
|
@ -139,14 +158,14 @@ internal class DefaultOnboardingVisaComponent @AssistedInject constructor(
|
|||
appComponentContext = factoryContext,
|
||||
config = OnboardingVisaOtherWalletComponent.Config(
|
||||
scanResponse = model.currentScanResponse.value,
|
||||
visaDataForApprove = route.visaDataForApprove,
|
||||
preparationDataForApprove = route.preparationDataForApprove,
|
||||
),
|
||||
params = OnboardingVisaOtherWalletComponent.Params(
|
||||
childParams = childParams,
|
||||
onDone = {
|
||||
model.stackNavigation.push(
|
||||
OnboardingVisaRoute.PinCode(
|
||||
activationOrderId = route.visaDataForApprove.dataToSign.request.orderId,
|
||||
activationOrderInfo = TODO(),
|
||||
),
|
||||
)
|
||||
},
|
||||
|
|
@ -156,7 +175,7 @@ internal class DefaultOnboardingVisaComponent @AssistedInject constructor(
|
|||
appComponentContext = factoryContext,
|
||||
config = OnboardingVisaPinCodeComponent.Config(
|
||||
scanResponse = model.currentScanResponse.value,
|
||||
activationOrderId = route.activationOrderId,
|
||||
activationOrderInfo = route.activationOrderInfo,
|
||||
),
|
||||
params = OnboardingVisaPinCodeComponent.Params(
|
||||
childParams = childParams,
|
||||
|
|
@ -166,7 +185,8 @@ internal class DefaultOnboardingVisaComponent @AssistedInject constructor(
|
|||
is OnboardingVisaRoute.TangemWalletApproveOption -> OnboardingVisaApproveComponent(
|
||||
appComponentContext = factoryContext,
|
||||
config = OnboardingVisaApproveComponent.Config(
|
||||
visaDataForApprove = route.visaDataForApprove,
|
||||
customerWalletCardId = route.foundWalletCardId,
|
||||
preparationDataForApprove = route.preparationDataForApprove,
|
||||
scanResponse = model.currentScanResponse.value,
|
||||
),
|
||||
params = OnboardingVisaApproveComponent.Params(
|
||||
|
|
|
|||
|
|
@ -8,11 +8,12 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.core.ui.security.DisableScreenshotsDisposableEffect
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.visa.model.VisaDataForApprove
|
||||
import com.tangem.features.onboarding.v2.visa.impl.DefaultOnboardingVisaComponent
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.accesscode.model.OnboardingVisaAccessCodeModel
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.accesscode.ui.OnboardingVisaAccessCode
|
||||
import com.tangem.features.onboarding.v2.visa.impl.common.ActivationReadyEvent
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal class OnboardingVisaAccessCodeComponent(
|
||||
|
|
@ -43,7 +44,7 @@ internal class OnboardingVisaAccessCodeComponent(
|
|||
|
||||
BackHandler(onBack = model::onBack)
|
||||
|
||||
// DisableScreenshotsDisposableEffect()
|
||||
DisableScreenshotsDisposableEffect()
|
||||
|
||||
OnboardingVisaAccessCode(state, modifier)
|
||||
}
|
||||
|
|
@ -54,12 +55,6 @@ internal class OnboardingVisaAccessCodeComponent(
|
|||
|
||||
data class Params(
|
||||
val childParams: DefaultOnboardingVisaComponent.ChildParams,
|
||||
val onDone: (DoneEvent) -> Unit,
|
||||
)
|
||||
|
||||
data class DoneEvent(
|
||||
val visaDataForApprove: VisaDataForApprove,
|
||||
val walletFound: Boolean,
|
||||
val newScanResponse: ScanResponse,
|
||||
val onDone: (ActivationReadyEvent) -> Unit,
|
||||
)
|
||||
}
|
||||
|
|
@ -7,17 +7,14 @@ 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.common.visa.VisaUtilities
|
||||
import com.tangem.domain.common.visa.VisaWalletPublicKeyUtility
|
||||
import com.tangem.domain.visa.model.VisaCardActivationStatus
|
||||
import com.tangem.domain.visa.model.VisaCardId
|
||||
import com.tangem.domain.visa.model.VisaCustomerWalletDataToSignRequest
|
||||
import com.tangem.domain.visa.model.VisaDataForApprove
|
||||
import com.tangem.domain.visa.repository.VisaActivationRepository
|
||||
import com.tangem.domain.visa.repository.VisaAuthRepository
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.accesscode.OnboardingVisaAccessCodeComponent
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.accesscode.ui.state.OnboardingVisaAccessCodeUM
|
||||
import com.tangem.features.onboarding.v2.visa.impl.common.ActivationReadyEvent
|
||||
import com.tangem.sdk.api.TangemSdkManager
|
||||
import com.tangem.sdk.api.visa.VisaCardActivationTaskMode
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -37,7 +34,6 @@ internal class OnboardingVisaAccessCodeModel @Inject constructor(
|
|||
@Suppress("UnusedPrivateMember")
|
||||
private val tangemSdkManager: TangemSdkManager,
|
||||
private val visaAuthRepository: VisaAuthRepository,
|
||||
private val getWalletsUseCase: GetWalletsUseCase,
|
||||
) : Model() {
|
||||
|
||||
private val params: OnboardingVisaAccessCodeComponent.Config = paramsContainer.require()
|
||||
|
|
@ -55,7 +51,7 @@ internal class OnboardingVisaAccessCodeModel @Inject constructor(
|
|||
|
||||
val uiState = _uiState.asStateFlow()
|
||||
val onBack = MutableSharedFlow<Unit>()
|
||||
val onDone = MutableSharedFlow<OnboardingVisaAccessCodeComponent.DoneEvent>()
|
||||
val onDone = MutableSharedFlow<ActivationReadyEvent>()
|
||||
|
||||
fun onBack() {
|
||||
if (uiState.value.buttonLoading) return
|
||||
|
|
@ -166,61 +162,25 @@ internal class OnboardingVisaAccessCodeModel @Inject constructor(
|
|||
return@launch
|
||||
}
|
||||
|
||||
// load data to sign by customer wallet for the next step
|
||||
val dataToSign = runCatching {
|
||||
visaActivationRepository.getCustomerWalletAcceptanceData(
|
||||
VisaCustomerWalletDataToSignRequest(
|
||||
orderId = result.data.signedActivationData.dataToSign.request.orderId,
|
||||
cardWalletAddress = result.data.signedActivationData.cardWalletAddress,
|
||||
),
|
||||
)
|
||||
}.getOrElse {
|
||||
loading(false)
|
||||
// TODO show alert
|
||||
return@launch
|
||||
}
|
||||
|
||||
val targetAddress = result.data.signedActivationData.dataToSign.request.customerWalletAddress
|
||||
val foundCardId = tryToFindExistingWalletCardId(targetAddress)
|
||||
|
||||
modelScope.launch {
|
||||
onDone.emit(
|
||||
OnboardingVisaAccessCodeComponent.DoneEvent(
|
||||
visaDataForApprove = VisaDataForApprove(
|
||||
targetAddress = targetAddress,
|
||||
customerWalletCardId = foundCardId,
|
||||
dataToSign = dataToSign,
|
||||
ActivationReadyEvent(
|
||||
customerWalletDataToSignRequest = VisaCustomerWalletDataToSignRequest(
|
||||
orderId = result.data.signedActivationData.dataToSign.request.orderId,
|
||||
cardWalletAddress = result.data.signedActivationData.cardWalletAddress,
|
||||
),
|
||||
walletFound = foundCardId != null,
|
||||
newScanResponse = params.scanResponse.copy(
|
||||
card = result.data.newCardDTO,
|
||||
),
|
||||
customerWalletTargetAddress = targetAddress,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryToFindExistingWalletCardId(targetAddress: String): String? {
|
||||
val wallets = getWalletsUseCase.invokeSync().filter { it.isLocked.not() }
|
||||
|
||||
return wallets.firstOrNull { wallet ->
|
||||
wallet.scanResponse.card.wallets.any {
|
||||
val derivedKey = it.derivedKeys[VisaUtilities.visaDefaultDerivationPath] ?: return@any false
|
||||
|
||||
VisaWalletPublicKeyUtility.validateExtendedPublicKey(
|
||||
targetAddress = targetAddress,
|
||||
extendedPublicKey = derivedKey,
|
||||
).onLeft {
|
||||
return@any VisaWalletPublicKeyUtility.findKeyWithoutDerivation(
|
||||
targetAddress = targetAddress,
|
||||
card = wallet.scanResponse.card,
|
||||
).isRight()
|
||||
}.isRight()
|
||||
}
|
||||
}?.cardId
|
||||
}
|
||||
|
||||
private fun loading(state: Boolean) {
|
||||
_uiState.update { it.copy(buttonLoading = state) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ 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.VisaDataForApprove
|
||||
import com.tangem.features.onboarding.v2.visa.impl.DefaultOnboardingVisaComponent
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.approve.model.OnboardingVisaApproveModel
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.approve.ui.OnboardingVisaApprove
|
||||
import com.tangem.features.onboarding.v2.visa.impl.common.PreparationDataForApprove
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal class OnboardingVisaApproveComponent(
|
||||
|
|
@ -43,7 +43,8 @@ internal class OnboardingVisaApproveComponent(
|
|||
}
|
||||
|
||||
data class Config(
|
||||
val visaDataForApprove: VisaDataForApprove,
|
||||
val customerWalletCardId: String?,
|
||||
val preparationDataForApprove: PreparationDataForApprove,
|
||||
val scanResponse: ScanResponse,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ 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.VisaCardId
|
||||
import com.tangem.domain.visa.model.VisaDataForApprove
|
||||
import com.tangem.domain.visa.repository.VisaActivationRepository
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.approve.OnboardingVisaApproveComponent
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.approve.ui.state.OnboardingVisaApproveUM
|
||||
|
|
@ -51,8 +52,20 @@ internal class OnboardingVisaApproveModel @Inject constructor(
|
|||
loading(true)
|
||||
|
||||
modelScope.launch {
|
||||
val dataToSign = runCatching {
|
||||
visaActivationRepository.getCustomerWalletAcceptanceData(params.preparationDataForApprove.request)
|
||||
}.getOrElse {
|
||||
loading(false)
|
||||
// TODO show dialog
|
||||
return@launch
|
||||
}
|
||||
|
||||
val result = tangemSdkManager.visaCustomerWalletApprove(
|
||||
visaDataForApprove = params.visaDataForApprove,
|
||||
visaDataForApprove = VisaDataForApprove(
|
||||
customerWalletCardId = params.customerWalletCardId,
|
||||
targetAddress = params.preparationDataForApprove.customerWalletAddress,
|
||||
dataToSign = dataToSign,
|
||||
),
|
||||
) as? CompletionResult.Success ?: run {
|
||||
loading(false)
|
||||
// TODO show dialog
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ internal class OnboardingVisaInProgressModel @Inject constructor(
|
|||
visaActivationRepository.getActivationRemoteStateLongPoll()
|
||||
}.getOrNull()
|
||||
|
||||
if (result == VisaActivationRemoteState.WaitingPinCode) {
|
||||
if (result is VisaActivationRemoteState.WaitingPinCode) {
|
||||
onDone.emit(Unit)
|
||||
break
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ 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.VisaDataForApprove
|
||||
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
|
||||
import com.tangem.features.onboarding.v2.visa.impl.common.PreparationDataForApprove
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
|
|
@ -45,7 +45,7 @@ internal class OnboardingVisaOtherWalletComponent(
|
|||
|
||||
data class Config(
|
||||
val scanResponse: ScanResponse,
|
||||
val visaDataForApprove: VisaDataForApprove,
|
||||
val preparationDataForApprove: PreparationDataForApprove,
|
||||
)
|
||||
|
||||
data class Params(
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ internal class OnboardingVisaOtherWalletModel @Inject constructor(
|
|||
visaActivationRepository.getActivationRemoteStateLongPoll()
|
||||
}.getOrNull()
|
||||
|
||||
if (result == VisaActivationRemoteState.WaitingPinCode) {
|
||||
if (result is VisaActivationRemoteState.WaitingPinCode) {
|
||||
onDone.emit(Unit)
|
||||
break
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import com.tangem.core.decompose.model.getOrCreateModel
|
|||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.core.ui.security.DisableScreenshotsDisposableEffect
|
||||
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.pincode.model.OnboardingVisaPinCodeModel
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.pincode.ui.OnboardingVisaPinCode
|
||||
|
|
@ -46,7 +47,7 @@ internal class OnboardingVisaPinCodeComponent(
|
|||
|
||||
data class Config(
|
||||
val scanResponse: ScanResponse,
|
||||
val activationOrderId: String,
|
||||
val activationOrderInfo: VisaActivationOrderInfo,
|
||||
)
|
||||
|
||||
data class Params(
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ internal class OnboardingVisaPinCodeModel @Inject constructor(
|
|||
setVisaPinCodeUseCase(
|
||||
pinCode = _uiState.value.pinCode,
|
||||
visaCardId = visaCardId,
|
||||
activationOrderId = params.activationOrderId,
|
||||
activationOrderId = params.activationOrderInfo.orderId,
|
||||
).onLeft {
|
||||
loading(false)
|
||||
return@launch
|
||||
|
|
|
|||
|
|
@ -2,32 +2,39 @@ package com.tangem.features.onboarding.v2.visa.impl.child.welcome
|
|||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
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.VisaActivationInput
|
||||
import com.tangem.domain.visa.model.VisaCardWalletDataToSignRequest
|
||||
import com.tangem.features.onboarding.v2.visa.impl.DefaultOnboardingVisaComponent
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.welcome.model.OnboardingVisaWelcomeModel
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.welcome.ui.OnboardingVisaWelcome
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.welcome.ui.state.OnboardingVisaWelcomeUM
|
||||
import com.tangem.features.onboarding.v2.visa.impl.common.ActivationReadyEvent
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal class OnboardingVisaWelcomeComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
config: Config,
|
||||
private val params: Params,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: OnboardingVisaWelcomeModel = getOrCreateModel(config)
|
||||
|
||||
init {
|
||||
componentScope.launch {
|
||||
model.onDone.collect { params.onDone(it) }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state = remember(this) {
|
||||
OnboardingVisaWelcomeUM(
|
||||
mode = if (params.isWelcomeBack) {
|
||||
OnboardingVisaWelcomeUM.Mode.WelcomeBack
|
||||
} else {
|
||||
OnboardingVisaWelcomeUM.Mode.Hello
|
||||
},
|
||||
userName = "User", // TODO:
|
||||
onContinueClick = params.onDone,
|
||||
)
|
||||
}
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
BackHandler(onBack = remember(this) { { params.childParams.onBack() } })
|
||||
|
||||
|
|
@ -37,9 +44,26 @@ internal class OnboardingVisaWelcomeComponent(
|
|||
)
|
||||
}
|
||||
|
||||
sealed class Config {
|
||||
data object Welcome : Config()
|
||||
|
||||
data class WelcomeBack(
|
||||
val activationInput: VisaActivationInput,
|
||||
val dataToSignRequest: VisaCardWalletDataToSignRequest,
|
||||
val scanResponse: ScanResponse,
|
||||
) : Config()
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val childParams: DefaultOnboardingVisaComponent.ChildParams,
|
||||
val isWelcomeBack: Boolean,
|
||||
val onDone: () -> Unit,
|
||||
val onDone: (DoneEvent) -> Unit,
|
||||
)
|
||||
|
||||
sealed class DoneEvent {
|
||||
data object WelcomeDone : DoneEvent()
|
||||
|
||||
data class WelcomeBackDone(
|
||||
val activationReadyEvent: ActivationReadyEvent,
|
||||
) : DoneEvent()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
package com.tangem.features.onboarding.v2.visa.impl.child.welcome.model
|
||||
|
||||
import com.tangem.common.CompletionResult
|
||||
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.VisaCardId
|
||||
import com.tangem.domain.visa.model.VisaCustomerWalletDataToSignRequest
|
||||
import com.tangem.domain.visa.repository.VisaActivationRepository
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.welcome.OnboardingVisaWelcomeComponent.Config
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.welcome.OnboardingVisaWelcomeComponent.DoneEvent
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.welcome.ui.state.OnboardingVisaWelcomeUM
|
||||
import com.tangem.features.onboarding.v2.visa.impl.common.ActivationReadyEvent
|
||||
import com.tangem.sdk.api.TangemSdkManager
|
||||
import com.tangem.sdk.api.visa.VisaCardActivationTaskMode
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@ComponentScoped
|
||||
internal class OnboardingVisaWelcomeModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val tangemSdkManager: TangemSdkManager,
|
||||
private val visaActivationRepositoryFactory: VisaActivationRepository.Factory,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<Config>()
|
||||
|
||||
private val _uiState = MutableStateFlow(getInitialState())
|
||||
|
||||
val uiState = _uiState.asStateFlow()
|
||||
val onDone = MutableSharedFlow<DoneEvent>()
|
||||
|
||||
private fun getInitialState(): OnboardingVisaWelcomeUM {
|
||||
return OnboardingVisaWelcomeUM(
|
||||
mode = if (params is Config.WelcomeBack) {
|
||||
OnboardingVisaWelcomeUM.Mode.WelcomeBack
|
||||
} else {
|
||||
OnboardingVisaWelcomeUM.Mode.Hello
|
||||
},
|
||||
onContinueClick = ::onContinueClick,
|
||||
)
|
||||
}
|
||||
|
||||
private fun onContinueClick() {
|
||||
if (params !is Config.WelcomeBack) {
|
||||
modelScope.launch { onDone.emit(DoneEvent.WelcomeDone) }
|
||||
return
|
||||
}
|
||||
|
||||
val visaActivationRepository = visaActivationRepositoryFactory.create(
|
||||
VisaCardId(
|
||||
cardId = params.scanResponse.card.cardId,
|
||||
cardPublicKey = params.scanResponse.card.cardPublicKey.toHexString(),
|
||||
),
|
||||
)
|
||||
|
||||
loading(true)
|
||||
|
||||
modelScope.launch {
|
||||
val dataToSignByCardWallet = runCatching {
|
||||
visaActivationRepository.getCardWalletAcceptanceData(params.dataToSignRequest)
|
||||
}.getOrElse {
|
||||
loading(false)
|
||||
// TODO show alert
|
||||
return@launch
|
||||
}
|
||||
|
||||
val result = tangemSdkManager.activateVisaCard(
|
||||
mode = VisaCardActivationTaskMode.SignOnly(dataToSignByCardWallet = dataToSignByCardWallet),
|
||||
activationInput = params.activationInput,
|
||||
) as? CompletionResult.Success ?: run {
|
||||
loading(false)
|
||||
// TODO show alert
|
||||
return@launch
|
||||
}
|
||||
|
||||
runCatching {
|
||||
visaActivationRepository.activateCard(result.data.signedActivationData)
|
||||
}.onFailure {
|
||||
loading(false)
|
||||
// TODO show alert
|
||||
return@launch
|
||||
}
|
||||
|
||||
val targetAddress = result.data.signedActivationData.dataToSign.request.customerWalletAddress
|
||||
|
||||
modelScope.launch {
|
||||
onDone.emit(
|
||||
DoneEvent.WelcomeBackDone(
|
||||
ActivationReadyEvent(
|
||||
customerWalletDataToSignRequest = VisaCustomerWalletDataToSignRequest(
|
||||
orderId = result.data.signedActivationData.dataToSign.request.orderId,
|
||||
cardWalletAddress = result.data.signedActivationData.cardWalletAddress,
|
||||
),
|
||||
newScanResponse = params.scanResponse.copy(
|
||||
card = result.data.newCardDTO,
|
||||
),
|
||||
customerWalletTargetAddress = targetAddress,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loading(state: Boolean) {
|
||||
_uiState.update { it.copy(continueButtonLoading = state) }
|
||||
}
|
||||
}
|
||||
|
|
@ -81,7 +81,8 @@ internal fun OnboardingVisaWelcome(state: OnboardingVisaWelcomeUM, modifier: Mod
|
|||
modifier = Modifier
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
text = "Start Activation",
|
||||
text = "Continue Activation",
|
||||
showProgress = state.continueButtonLoading,
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
onClick = state.onContinueClick,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.features.onboarding.v2.visa.impl.child.welcome.ui.state
|
|||
internal data class OnboardingVisaWelcomeUM(
|
||||
val mode: Mode = Mode.Hello,
|
||||
val userName: String = "",
|
||||
val continueButtonLoading: Boolean = false,
|
||||
val onContinueClick: () -> Unit = {},
|
||||
) {
|
||||
enum class Mode {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.features.onboarding.v2.visa.impl.common
|
||||
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.visa.model.VisaCustomerWalletDataToSignRequest
|
||||
|
||||
internal data class ActivationReadyEvent(
|
||||
val customerWalletDataToSignRequest: VisaCustomerWalletDataToSignRequest,
|
||||
val customerWalletTargetAddress: String,
|
||||
val newScanResponse: ScanResponse,
|
||||
)
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.features.onboarding.v2.visa.impl.common
|
||||
|
||||
import com.tangem.domain.visa.model.VisaCustomerWalletDataToSignRequest
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
internal data class PreparationDataForApprove(
|
||||
val customerWalletAddress: String,
|
||||
val request: VisaCustomerWalletDataToSignRequest,
|
||||
)
|
||||
|
|
@ -10,6 +10,7 @@ import com.tangem.features.onboarding.v2.visa.impl.child.choosewallet.model.Onbo
|
|||
import com.tangem.features.onboarding.v2.visa.impl.child.inprogress.model.OnboardingVisaInProgressModel
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.otherwallet.model.OnboardingVisaOtherWalletModel
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.pincode.model.OnboardingVisaPinCodeModel
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.welcome.model.OnboardingVisaWelcomeModel
|
||||
import com.tangem.features.onboarding.v2.visa.impl.model.OnboardingVisaModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
|
|
@ -66,4 +67,9 @@ internal interface ModelModule {
|
|||
@IntoMap
|
||||
@ClassKey(OnboardingVisaModel::class)
|
||||
fun provideModel7(model: OnboardingVisaModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(OnboardingVisaWelcomeModel::class)
|
||||
fun provideModel8(model: OnboardingVisaWelcomeModel): Model
|
||||
}
|
||||
|
|
@ -7,12 +7,17 @@ import com.arkivanov.decompose.router.stack.push
|
|||
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.common.visa.VisaUtilities
|
||||
import com.tangem.domain.common.visa.VisaWalletPublicKeyUtility
|
||||
import com.tangem.domain.visa.model.VisaActivationRemoteState
|
||||
import com.tangem.domain.visa.model.VisaCardActivationStatus
|
||||
import com.tangem.domain.visa.model.VisaCustomerWalletDataToSignRequest
|
||||
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.accesscode.OnboardingVisaAccessCodeComponent
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.choosewallet.OnboardingVisaChooseWalletComponent
|
||||
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
|
||||
|
|
@ -25,6 +30,7 @@ import javax.inject.Inject
|
|||
internal class OnboardingVisaModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val getWalletsUseCase: GetWalletsUseCase,
|
||||
) : Model() {
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
|
|
@ -49,37 +55,24 @@ internal class OnboardingVisaModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
fun navigateFromWelcome(route: OnboardingVisaRoute.Welcome) {
|
||||
if (route.isWelcomeBack) {
|
||||
val scanResponse = _currentScanResponse.value
|
||||
val activationStatus = scanResponse.visaCardActivationStatus
|
||||
as? VisaCardActivationStatus.ActivationStarted
|
||||
?: error("Activation status is not correct for welcome back route")
|
||||
|
||||
when (activationStatus.remoteState) {
|
||||
is VisaActivationRemoteState.CardWalletSignatureRequired,
|
||||
is VisaActivationRemoteState.CustomerWalletSignatureRequired,
|
||||
-> OnboardingVisaRoute.AccessCode
|
||||
VisaActivationRemoteState.WaitingPinCode -> OnboardingVisaRoute.PinCode
|
||||
VisaActivationRemoteState.WaitingForActivationFinishing -> OnboardingVisaRoute.InProgress
|
||||
VisaActivationRemoteState.PaymentAccountDeploying -> OnboardingVisaRoute.InProgress
|
||||
else -> error("Remote state is not correct for welcome back route")
|
||||
}
|
||||
} else {
|
||||
stackNavigation.push(OnboardingVisaRoute.AccessCode)
|
||||
}
|
||||
}
|
||||
|
||||
fun navigateFromAccessCode(result: OnboardingVisaAccessCodeComponent.DoneEvent) {
|
||||
fun navigateFromActivationScreen(result: ActivationReadyEvent) {
|
||||
_currentScanResponse.value = result.newScanResponse
|
||||
val foundWalletCardId = tryToFindExistingWalletCardId(result.customerWalletTargetAddress)
|
||||
val preparationDataForApprove = PreparationDataForApprove(
|
||||
customerWalletAddress = result.customerWalletTargetAddress,
|
||||
request = result.customerWalletDataToSignRequest,
|
||||
)
|
||||
stackNavigation.push(
|
||||
if (result.walletFound) {
|
||||
if (foundWalletCardId != null) {
|
||||
OnboardingVisaRoute.TangemWalletApproveOption(
|
||||
visaDataForApprove = result.visaDataForApprove,
|
||||
foundWalletCardId = foundWalletCardId,
|
||||
preparationDataForApprove = preparationDataForApprove,
|
||||
allowNavigateBack = false,
|
||||
)
|
||||
} else {
|
||||
OnboardingVisaRoute.ChooseWallet(result.visaDataForApprove)
|
||||
OnboardingVisaRoute.ChooseWallet(
|
||||
preparationDataForApprove = preparationDataForApprove,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -92,11 +85,14 @@ internal class OnboardingVisaModel @Inject constructor(
|
|||
when (event) {
|
||||
OnboardingVisaChooseWalletComponent.Params.Event.TangemWallet ->
|
||||
OnboardingVisaRoute.TangemWalletApproveOption(
|
||||
visaDataForApprove = route.visaDataForApprove,
|
||||
preparationDataForApprove = route.preparationDataForApprove,
|
||||
foundWalletCardId = null,
|
||||
allowNavigateBack = true,
|
||||
)
|
||||
OnboardingVisaChooseWalletComponent.Params.Event.OtherWallet ->
|
||||
OnboardingVisaRoute.OtherWalletApproveOption(route.visaDataForApprove)
|
||||
OnboardingVisaRoute.OtherWalletApproveOption(
|
||||
preparationDataForApprove = route.preparationDataForApprove,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -116,6 +112,7 @@ internal class OnboardingVisaModel @Inject constructor(
|
|||
stackNavigation.pop()
|
||||
}
|
||||
}
|
||||
is OnboardingVisaRoute.WelcomeBack,
|
||||
is OnboardingVisaRoute.Welcome,
|
||||
is OnboardingVisaRoute.ChooseWallet,
|
||||
OnboardingVisaRoute.InProgress,
|
||||
|
|
@ -129,9 +126,72 @@ internal class OnboardingVisaModel @Inject constructor(
|
|||
val activationStatus = params.scanResponse.visaCardActivationStatus
|
||||
|
||||
return when (activationStatus) {
|
||||
is VisaCardActivationStatus.ActivationStarted -> OnboardingVisaRoute.Welcome(isWelcomeBack = true)
|
||||
is VisaCardActivationStatus.NotStartedActivation -> OnboardingVisaRoute.Welcome(isWelcomeBack = false)
|
||||
is VisaCardActivationStatus.ActivationStarted -> {
|
||||
when (val remoteState = activationStatus.remoteState) {
|
||||
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.Activated,
|
||||
VisaActivationRemoteState.BlockedForActivation,
|
||||
-> error("Activation status is not correct for onboarding flow")
|
||||
}
|
||||
}
|
||||
is VisaCardActivationStatus.NotStartedActivation -> OnboardingVisaRoute.Welcome
|
||||
else -> error("Visa activation status is not correct for onboarding flow")
|
||||
}
|
||||
}
|
||||
|
||||
private fun VisaActivationRemoteState.CustomerWalletSignatureRequired.getRoute(
|
||||
activationStatus: VisaCardActivationStatus.ActivationStarted,
|
||||
): OnboardingVisaRoute {
|
||||
val foundWalletCardId = tryToFindExistingWalletCardId(this.activationOrderInfo.customerWalletAddress)
|
||||
val request = VisaCustomerWalletDataToSignRequest(
|
||||
orderId = this.activationOrderInfo.orderId,
|
||||
cardWalletAddress = activationStatus.cardWalletAddress,
|
||||
)
|
||||
val preparationDataForApprove = PreparationDataForApprove(
|
||||
customerWalletAddress = this.activationOrderInfo.customerWalletAddress,
|
||||
request = request,
|
||||
)
|
||||
|
||||
return if (foundWalletCardId != null) {
|
||||
OnboardingVisaRoute.TangemWalletApproveOption(
|
||||
preparationDataForApprove = preparationDataForApprove,
|
||||
foundWalletCardId = foundWalletCardId,
|
||||
allowNavigateBack = false,
|
||||
)
|
||||
} else {
|
||||
OnboardingVisaRoute.ChooseWallet(
|
||||
preparationDataForApprove = preparationDataForApprove,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryToFindExistingWalletCardId(targetAddress: String): String? {
|
||||
val wallets = getWalletsUseCase.invokeSync().filter { it.isLocked.not() }
|
||||
|
||||
return wallets.firstOrNull { wallet ->
|
||||
wallet.scanResponse.card.wallets.any {
|
||||
val derivedKey = it.derivedKeys[VisaUtilities.visaDefaultDerivationPath] ?: return@any false
|
||||
|
||||
VisaWalletPublicKeyUtility.validateExtendedPublicKey(
|
||||
targetAddress = targetAddress,
|
||||
extendedPublicKey = derivedKey,
|
||||
).onLeft {
|
||||
return@any VisaWalletPublicKeyUtility.findKeyWithoutDerivation(
|
||||
targetAddress = targetAddress,
|
||||
card = wallet.scanResponse.card,
|
||||
).isRight()
|
||||
}.isRight()
|
||||
}
|
||||
}?.cardId
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +1,46 @@
|
|||
package com.tangem.features.onboarding.v2.visa.impl.route
|
||||
|
||||
import com.tangem.domain.visa.model.VisaDataForApprove
|
||||
import com.tangem.domain.visa.model.VisaActivationInput
|
||||
import com.tangem.domain.visa.model.VisaActivationOrderInfo
|
||||
import com.tangem.domain.visa.model.VisaCardWalletDataToSignRequest
|
||||
import com.tangem.features.onboarding.v2.visa.impl.common.PreparationDataForApprove
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
sealed class OnboardingVisaRoute {
|
||||
internal sealed class OnboardingVisaRoute {
|
||||
|
||||
@Serializable
|
||||
data class Welcome(val isWelcomeBack: Boolean) : OnboardingVisaRoute()
|
||||
data object Welcome : OnboardingVisaRoute()
|
||||
|
||||
@Serializable
|
||||
data class WelcomeBack(
|
||||
val activationInput: VisaActivationInput,
|
||||
val dataToSignByCardWalletRequest: VisaCardWalletDataToSignRequest,
|
||||
) : OnboardingVisaRoute()
|
||||
|
||||
@Serializable
|
||||
data object AccessCode : OnboardingVisaRoute()
|
||||
|
||||
@Serializable
|
||||
data class ChooseWallet(val visaDataForApprove: VisaDataForApprove) : OnboardingVisaRoute()
|
||||
data class ChooseWallet(
|
||||
val preparationDataForApprove: PreparationDataForApprove,
|
||||
) : OnboardingVisaRoute()
|
||||
|
||||
@Serializable
|
||||
data class TangemWalletApproveOption(
|
||||
val visaDataForApprove: VisaDataForApprove,
|
||||
val preparationDataForApprove: PreparationDataForApprove,
|
||||
val foundWalletCardId: String?,
|
||||
val allowNavigateBack: Boolean,
|
||||
) : OnboardingVisaRoute()
|
||||
|
||||
@Serializable
|
||||
data class OtherWalletApproveOption(val visaDataForApprove: VisaDataForApprove) : OnboardingVisaRoute()
|
||||
data class OtherWalletApproveOption(
|
||||
val preparationDataForApprove: PreparationDataForApprove,
|
||||
) : OnboardingVisaRoute()
|
||||
|
||||
@Serializable
|
||||
data object InProgress : OnboardingVisaRoute()
|
||||
|
||||
@Serializable
|
||||
data class PinCode(val activationOrderId: String) : OnboardingVisaRoute()
|
||||
data class PinCode(val activationOrderInfo: VisaActivationOrderInfo) : OnboardingVisaRoute()
|
||||
}
|
||||
|
|
@ -2,7 +2,9 @@ package com.tangem.features.onboarding.v2.visa.impl.route
|
|||
|
||||
@Suppress("MagicNumber")
|
||||
internal fun OnboardingVisaRoute.stepNum() = when (this) {
|
||||
is OnboardingVisaRoute.Welcome -> 1
|
||||
is OnboardingVisaRoute.Welcome,
|
||||
is OnboardingVisaRoute.WelcomeBack,
|
||||
-> 1
|
||||
OnboardingVisaRoute.AccessCode -> 2
|
||||
is OnboardingVisaRoute.ChooseWallet -> 3
|
||||
is OnboardingVisaRoute.OtherWalletApproveOption -> 4
|
||||
|
|
|
|||
|
|
@ -10,5 +10,7 @@ internal fun OnboardingVisaRoute.screenTitle(): TextReference = when (this) {
|
|||
is OnboardingVisaRoute.OtherWalletApproveOption -> TextReference.Str("Wallet connection")
|
||||
is OnboardingVisaRoute.PinCode -> TextReference.Str("PIN code")
|
||||
is OnboardingVisaRoute.TangemWalletApproveOption -> TextReference.Str("Wallet connection")
|
||||
is OnboardingVisaRoute.Welcome -> TextReference.Str("Getting started")
|
||||
is OnboardingVisaRoute.Welcome,
|
||||
is OnboardingVisaRoute.WelcomeBack,
|
||||
-> TextReference.Str("Getting started")
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue