Updated on 2026-08-14

This commit is contained in:
Tangem 2022-10-22 18:24:47 +05:00
parent ad95296768
commit 2a754c50cf
23 changed files with 584 additions and 202 deletions

View file

@ -111,7 +111,8 @@ private fun onScanSuccess(scanResponse: ScanResponse) {
if (scanResponse.isSaltPay()) {
if (scanResponse.isSaltPayVisa()) {
scope.launch {
val result = OnboardingSaltPayHelper.isOnboardingCase(scanResponse)
val (manager, config) = OnboardingSaltPayState.initDependency(scanResponse)
val result = OnboardingSaltPayHelper.isOnboardingCase(scanResponse, manager)
delay(500)
withMainContext {
when (result) {
@ -119,10 +120,8 @@ private fun onScanSuccess(scanResponse: ScanResponse) {
val isOnboardingCase = result.data
if (isOnboardingCase) {
store.dispatch(GlobalAction.Onboarding.Start(scanResponse, canSkipBackup = false))
val (manager, config) = OnboardingSaltPayState.initDependency(scanResponse)
store.dispatch(OnboardingSaltPayAction.Init.SetDependencies(manager, config))
store.dispatch(OnboardingSaltPayAction.Update)
// store.dispatch(OnboardingSaltPayAction.Init.DiscardBackupSteps)
navigateTo(AppScreen.OnboardingWallet)
} else {
navigateTo(AppScreen.Wallet)

View file

@ -3,10 +3,10 @@ package com.tangem.tap.features.onboarding
import com.tangem.common.services.Result
import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.extensions.successOr
import com.tangem.tap.features.onboarding.products.wallet.saltPay.SaltPayRegistrationManager
import com.tangem.tap.features.onboarding.products.wallet.saltPay.message.SaltPayRegistrationError
import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.OnboardingSaltPayState
import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.SaltPayRegistrationStep
import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.toSaltPayStep
import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.updateSaltPayStatus
/**
[REDACTED_AUTHOR]
@ -14,19 +14,22 @@ import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.toSaltPa
class OnboardingSaltPayHelper {
companion object {
suspend fun isOnboardingCase(scanResponse: ScanResponse): Result<Boolean> {
suspend fun isOnboardingCase(
scanResponse: ScanResponse,
manager: SaltPayRegistrationManager
): Result<Boolean> {
return try {
val (manager, config) = OnboardingSaltPayState.initDependency(scanResponse)
val registrationResponseItem = manager.checkRegistration().successOr { return it }
val registrationStep = registrationResponseItem.toSaltPayStep()
manager.checkHasGas().successOr { return it }
val status = manager.updateSaltPayStatus(
amountToClaim = null,
step = SaltPayRegistrationStep.None,
).successOr { return it }
val isRegistrationOnboardingCase = registrationStep != SaltPayRegistrationStep.Finished
val isBackupOnboardingCase = scanResponse.card.backupStatus?.isActive == false
Result.Success(isRegistrationOnboardingCase || isBackupOnboardingCase)
val isRegistrationCase = status.step != SaltPayRegistrationStep.ClaimSuccess
val isBackupCase = scanResponse.card.backupStatus?.isActive == false
Result.Success(isRegistrationCase || isBackupCase)
} catch (ex: Exception) {
Result.Failure(ex)
} catch (error: SaltPayRegistrationError){
} catch (error: SaltPayRegistrationError) {
Result.Failure(error)
}
}
@ -37,7 +40,7 @@ class OnboardingSaltPayHelper {
Result.Success(isBackupOnboardingCase)
} catch (ex: Exception) {
Result.Failure(ex)
} catch (error: SaltPayRegistrationError){
} catch (error: SaltPayRegistrationError) {
Result.Failure(error)
}
}
@ -47,7 +50,7 @@ class OnboardingSaltPayHelper {
Result.Success(false)
} catch (ex: Exception) {
Result.Failure(ex)
} catch (error: SaltPayRegistrationError){
} catch (error: SaltPayRegistrationError) {
Result.Failure(error)
}
}

View file

@ -4,7 +4,12 @@ import com.tangem.common.CompletionResult
import com.tangem.common.extensions.guard
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchErrorNotification
import com.tangem.tap.common.extensions.dispatchOpenUrl
import com.tangem.tap.common.extensions.getAddressData
import com.tangem.tap.common.extensions.getToUpUrl
import com.tangem.tap.common.extensions.onCardScanned
import com.tangem.tap.common.postUi
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.common.redux.AppState
@ -15,6 +20,7 @@ import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.hasWallets
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.onboarding.WalletBalanceFetcher
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.scope
@ -115,16 +121,16 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch
val balanceIsLoading = noteState.walletBalance.copy(
currency = Currency.Blockchain(
walletManager.wallet.blockchain,
walletManager.wallet.publicKey.derivationPath?.rawPath
walletManager.wallet.publicKey.derivationPath?.rawPath,
),
state = ProgressState.Loading,
error = null,
criticalError = null
criticalError = null,
)
store.dispatch(OnboardingNoteAction.Balance.Set(balanceIsLoading))
scope.launch {
val loadedBalance = onboardingManager.updateBalance(walletManager)
val loadedBalance = WalletBalanceFetcher().updateBalance(walletManager)
delay(if (isLoadedBefore) 0 else 300)
loadedBalance.criticalError?.let { store.dispatchErrorNotification(it) }
withMainContext {

View file

@ -30,7 +30,7 @@ data class OnboardingWalletState(
}
fun getMaxProgress(): Int = when {
isSaltPay -> 12
isSaltPay -> 14
else -> 6
}
@ -41,7 +41,6 @@ data class OnboardingWalletState(
when (backupState.backupStep) {
BackupStep.InitBackup -> 2
BackupStep.ScanOriginCard -> 3
BackupStep.AddBackupCards -> 4
BackupStep.EnterAccessCode -> 4
BackupStep.ReenterAccessCode -> 4
@ -52,13 +51,15 @@ data class OnboardingWalletState(
}
step == OnboardingWalletStep.SaltPay && isSaltPay -> {
when (onboardingSaltPayState!!.step) {
SaltPayRegistrationStep.NoGas -> 1
SaltPayRegistrationStep.None, SaltPayRegistrationStep.NoGas -> 0
SaltPayRegistrationStep.NeedPin -> 7
SaltPayRegistrationStep.CardRegistration -> 8
SaltPayRegistrationStep.KycIntro -> 9
SaltPayRegistrationStep.KycStart -> 10
SaltPayRegistrationStep.KycWaiting -> 11
SaltPayRegistrationStep.Finished -> getMaxProgress()
SaltPayRegistrationStep.Claim -> 12
SaltPayRegistrationStep.ClaimInProgress -> 13
SaltPayRegistrationStep.ClaimSuccess -> getMaxProgress()
}
}
step == OnboardingWalletStep.Done -> getMaxProgress()

View file

@ -7,6 +7,7 @@ import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.blockchain.common.TransactionSigner
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.extensions.SimpleResult
@ -42,6 +43,8 @@ class GnosisRegistrator(
else -> throw IllegalArgumentException("GnosisRegistrator supports only the SaltPay blockchain")
}
private val addressTreasureSafe = "0x8e9260a049d3Aa9ac60D0d4F27017320E0e2396B"
private val atomicNonce = AtomicLong()
suspend fun checkHasGas(): Result<Boolean> {
@ -142,6 +145,21 @@ class GnosisRegistrator(
return Result.Success(compiledEthereumTransaction)
}
suspend fun getAllowance(): Result<Amount> {
return walletManager.getAllowance(addressTreasureSafe, token)
}
suspend fun transferFrom(amountToClaim: BigDecimal, signer: TransactionSigner): Result<Unit> {
val amount = Amount(token, amountToClaim)
val feeAmount = walletManager.getFeeToTransferFrom(amount, addressTreasureSafe)
.extractFeeAmount().successOr { return it }
val transactionData = walletManager.createTransferFromTransaction(amount, feeAmount, addressTreasureSafe)
walletManager.transferFrom(transactionData, signer).successOr { return Result.Failure(it.error) }
return Result.Success(Unit)
}
private fun Result<List<Amount>>.extractFeeAmount(): Result<Amount> {
return when (this) {
is Result.Success -> {

View file

@ -2,6 +2,12 @@ package com.tangem.tap.features.onboarding.products.wallet.saltPay
import android.net.Uri
import com.tangem.blockchain.blockchains.ethereum.SignedEthereumTransaction
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.TransactionSigner
import com.tangem.blockchain.extensions.successOr
import com.tangem.common.extensions.guard
import com.tangem.common.extensions.isZero
import com.tangem.common.services.Result
import com.tangem.domain.common.extensions.successOr
import com.tangem.network.api.paymentology.AttestationResponse
@ -11,6 +17,8 @@ import com.tangem.network.api.paymentology.RegisterWalletRequest
import com.tangem.network.api.paymentology.RegisterWalletResponse
import com.tangem.network.api.paymentology.RegistrationResponse
import com.tangem.operations.attestation.AttestWalletKeyResponse
import com.tangem.tap.common.extensions.safeUpdate
import com.tangem.tap.domain.getFirstToken
import com.tangem.tap.domain.tokens.UserWalletId
import com.tangem.tap.features.onboarding.products.wallet.saltPay.message.SaltPayRegistrationError
import com.tangem.tap.persistence.SaltPayRegistrationStorage
@ -104,6 +112,38 @@ class SaltPayRegistrationManager(
return result
}
suspend fun getAmountToClaim(): Result<Amount> {
val amount = gnosisRegistrator.getAllowance().successOr {
return Result.Failure(SaltPayRegistrationError.FailedToGetFundsToClaim)
}
return if (amount.value == null || amount.value?.isZero() == true) {
Result.Failure(SaltPayRegistrationError.NoFundsToClaim)
} else {
Result.Success(amount)
}
}
suspend fun claim(amountToClaim: BigDecimal, signer: TransactionSigner): Result<Unit> {
val result = gnosisRegistrator.transferFrom(amountToClaim, signer).successOr {
return Result.Failure(SaltPayRegistrationError.ClaimTransactionFailed)
}
return Result.Success(Unit)
}
suspend fun getTokenAmount(): Result<BigDecimal> {
val wallet = gnosisRegistrator.walletManager.safeUpdate().successOr { return it }
val token = wallet.getFirstToken().guard {
throw UnsupportedOperationException()
}
val amountValue = wallet.amounts[AmountType.Token(token)]?.value.guard {
throw UnsupportedOperationException()
}
return Result.Success(amountValue)
}
fun makeRegistrationTask(challenge: ByteArray): SaltPayRegistrationTask {
return SaltPayRegistrationTask(
gnosisRegistrator = gnosisRegistrator,

View file

@ -42,8 +42,13 @@ sealed class SaltPayRegistrationError(
class CardDisabled(message: String?) : SaltPayRegistrationError(9, message)
class CardNotPassed(message: String?) : SaltPayRegistrationError(10, message)
object EmptyDynamicAttestResponse : SaltPayRegistrationError(11)
object EmptyBackupCardScanned : SaltPayRegistrationError(12) //
object WeakPin : SaltPayRegistrationError(13) //
object EmptyBackupCardScanned : SaltPayRegistrationError(12)
object WeakPin : SaltPayRegistrationError(13)
object FailedToGetFundsToClaim : SaltPayRegistrationError(14)
object NoFundsToClaim : SaltPayRegistrationError(15)
object ClaimTransactionFailed : SaltPayRegistrationError(16)
object NotClaimed : SaltPayRegistrationError(17)
companion object {
const val EMPTY_MESSAGE = ""

View file

@ -1,8 +1,10 @@
package com.tangem.tap.features.onboarding.products.wallet.saltPay.redux
import com.tangem.blockchain.common.Amount
import com.tangem.tap.features.onboarding.products.wallet.saltPay.SaltPayConfig
import com.tangem.tap.features.onboarding.products.wallet.saltPay.SaltPayRegistrationManager
import org.rekotlin.Action
import java.math.BigDecimal
/**
[REDACTED_AUTHOR]
@ -13,21 +15,25 @@ sealed class OnboardingSaltPayAction : Action {
val registrationManager: SaltPayRegistrationManager,
val saltPayConfig: SaltPayConfig,
) : Init()
// only for tests
object DiscardBackupSteps : Init()
}
data class SetIsBusy(val isBusy: Boolean) : OnboardingSaltPayAction()
data class SetInProgress(val isInProgress: Boolean) : OnboardingSaltPayAction()
data class SetClaimRefreshInProgress(val isInProgress: Boolean) : OnboardingSaltPayAction()
data class SetAccessCode(val accessCode: String?) : OnboardingSaltPayAction()
object Update : OnboardingSaltPayAction()
object RegisterCard : OnboardingSaltPayAction()
object KYCStart : OnboardingSaltPayAction()
object OnFinishKYC : OnboardingSaltPayAction()
object OpenUtorgKYC : OnboardingSaltPayAction()
object UtorgKYCRedirectSuccess : OnboardingSaltPayAction()
object RegisterKYC : OnboardingSaltPayAction()
object Claim : OnboardingSaltPayAction()
object RefreshClaim : OnboardingSaltPayAction()
data class TrySetPin(val pin: String) : OnboardingSaltPayAction()
data class SetPin(val pin: String) : OnboardingSaltPayAction()
data class SetStep(val newStep: SaltPayRegistrationStep? = null) : OnboardingSaltPayAction()
data class SetAmountToClaim(val amount: Amount?) : OnboardingSaltPayAction()
data class SetTokenBalance(val balanceValue: BigDecimal) : OnboardingSaltPayAction()
}

View file

@ -1,6 +1,7 @@
package com.tangem.tap.features.onboarding.products.wallet.saltPay.redux
import com.tangem.Message
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.extensions.successOr
import com.tangem.common.Filter
import com.tangem.common.core.TangemSdkError
@ -13,6 +14,8 @@ import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchErrorNotification
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TangemSigner
import com.tangem.tap.domain.TapError
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState
@ -22,6 +25,7 @@ import com.tangem.tap.features.onboarding.products.wallet.saltPay.dialog.SaltPay
import com.tangem.tap.features.onboarding.products.wallet.saltPay.message.SaltPayRegistrationError
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.tangemSdk
import com.tangem.tap.tangemSdkManager
import com.tangem.wallet.R
import kotlinx.coroutines.launch
@ -57,8 +61,55 @@ private fun handleOnboardingSaltPayAction(anyAction: Action, appState: () -> App
fun getState(): OnboardingSaltPayState = getOnboardingWalletState().onboardingSaltPayState!!
when (action) {
is OnboardingSaltPayAction.Update -> {
handleInProgress = true
val state = getState()
scope.launch {
val updateResult = state.saltPayManager.updateSaltPayStatus(
amountToClaim = state.amountToClaim,
step = state.step,
).successOr {
onException(it.error)
return@launch
}
handleInProgress = false
withMainContext {
// store.dispatch(OnboardingSaltPayAction.SetStep(updateResult.step))
store.dispatch(OnboardingSaltPayAction.SetStep(SaltPayRegistrationStep.KycWaiting))
}
}
// handleIsBusy = true
// scope.launch {
// checkGasIfNeeded(state.saltPayManager, state.step).successOr {
// onException(it.error)
// return@launch
// }
//
// registerKYCIfNeeded(state).successOr {
// onException(it.error)
// return@launch
// }
//
// val amount = getAmountToClaimIfNeeded(state)?.let {
// dispatchOnMain(OnboardingSaltPayAction.SetAmountToClaim(it))
// }
//
// val newStep = checkRegistration(state, amount != null).successOr {
// onException(it.error)
// return@launch
// }
//
// withMainContext {
// handleIsBusy = false
// store.dispatch(OnboardingSaltPayAction.SetStep(newStep))
// }
// }
}
is OnboardingSaltPayAction.RegisterCard -> {
handleIsBusy = true
handleInProgress = true
val state = getState()
scope.launch {
@ -107,18 +158,30 @@ private fun handleOnboardingSaltPayAction(anyAction: Action, appState: () -> App
}
withMainContext {
handleIsBusy = false
handleInProgress = false
store.dispatch(OnboardingSaltPayAction.SetStep(SaltPayRegistrationStep.KycIntro))
}
}
}
is OnboardingSaltPayAction.KYCStart -> {
is OnboardingSaltPayAction.OpenUtorgKYC -> {
store.dispatch(OnboardingSaltPayAction.SetStep(SaltPayRegistrationStep.KycStart))
}
is OnboardingSaltPayAction.OnFinishKYC -> {
is OnboardingSaltPayAction.UtorgKYCRedirectSuccess -> {
store.dispatch(OnboardingSaltPayAction.RegisterKYC)
}
is OnboardingSaltPayAction.RegisterKYC -> {
val state = getState()
handleInProgress = true
scope.launch {
registerKYCIfNeeded(getState()).successOr {
registerKYCIfNeeded(state.saltPayManager, state.step).successOr {
onException(it.error)
return@launch
}
handleInProgress = false
if (state.readyToClaim()) {
withMainContext { store.dispatch(OnboardingSaltPayAction.SetStep(SaltPayRegistrationStep.Claim)) }
} else {
onError(SaltPayRegistrationError.NoFundsToClaim)
}
}
}
@ -131,30 +194,58 @@ private fun handleOnboardingSaltPayAction(anyAction: Action, appState: () -> App
handleError = error
}
}
is OnboardingSaltPayAction.Update -> {
handleIsBusy = true
is OnboardingSaltPayAction.Claim -> {
val state = getState()
handleInProgress = true
scope.launch {
val amountToClaim = getAmountToClaimIfNeeded(state.saltPayManager, state.amountToClaim).guard {
onException(SaltPayRegistrationError.NoFundsToClaim)
return@launch
}
val scanResponse = store.state.globalState.scanResponse
?: store.state.globalState.onboardingState.onboardingManager?.scanResponse
val signer = TangemSigner(
card = scanResponse!!.card,
tangemSdk = tangemSdk,
initialMessage = Message(),
accessCode = state.accessCode,
) { signResponse ->
store.dispatch(
GlobalAction.UpdateWalletSignedHashes(
walletSignedHashes = signResponse.totalSignedHashes,
walletPublicKey = state.saltPayManager.walletPublicKey,
remainingSignatures = signResponse.remainingSignatures,
),
)
}
state.saltPayManager.claim(amountToClaim.value!!, signer).successOr {
dispatchOnMain(OnboardingSaltPayAction.SetStep(SaltPayRegistrationStep.Claim))
onException(it.error)
return@launch
}
dispatchOnMain(OnboardingSaltPayAction.SetStep(SaltPayRegistrationStep.ClaimInProgress))
dispatchOnMain(OnboardingSaltPayAction.RefreshClaim)
}
}
is OnboardingSaltPayAction.RefreshClaim -> {
handleInProgress = true
handleClaimRefreshInProgress = true
val state = getState()
scope.launch {
checkGasIfNeeded(state.saltPayManager, state.step).successOr {
onException(it.error)
val balance = state.saltPayManager.getTokenAmount().successOr {
handleException = it.error
handleClaimRefreshInProgress = false
return@launch
}
registerKYCIfNeeded(state).successOr {
onException(it.error)
return@launch
}
val newStep = checkRegistration(state).successOr {
onException(it.error)
return@launch
}
withMainContext {
handleIsBusy = false
store.dispatch(OnboardingSaltPayAction.SetStep(newStep))
}
handleInProgress = false
handleClaimRefreshInProgress = false
dispatchOnMain(OnboardingSaltPayAction.SetTokenBalance(balance))
dispatchOnMain(OnboardingSaltPayAction.SetStep(SaltPayRegistrationStep.ClaimSuccess))
}
}
else -> {
@ -163,22 +254,93 @@ private fun handleOnboardingSaltPayAction(anyAction: Action, appState: () -> App
}
}
private var handleIsBusy: Boolean = false
data class UpdateResult(
val step: SaltPayRegistrationStep = SaltPayRegistrationStep.None,
val amountToClaim: Amount? = null,
)
suspend fun SaltPayRegistrationManager.updateSaltPayStatus(
amountToClaim: Amount?,
step: SaltPayRegistrationStep,
): Result<UpdateResult> {
Timber.d("updateSaltPayStatus")
var updateResult = UpdateResult()
checkGasIfNeeded(this, step).successOr {
return Result.Failure(it.error)
}
registerKYCIfNeeded(this, step).successOr {
return Result.Failure(it.error)
}
val saltPayStep = checkRegistration(this).successOr {
return Result.Failure(it.error)
}
val fetchedAmountToClaim = getAmountToClaimIfNeeded(this, amountToClaim)
updateResult = when {
saltPayStep == SaltPayRegistrationStep.Claim && fetchedAmountToClaim == null -> {
updateResult.copy(
step = SaltPayRegistrationStep.ClaimSuccess,
amountToClaim = fetchedAmountToClaim,
)
}
else -> updateResult.copy(
step = saltPayStep,
amountToClaim = fetchedAmountToClaim,
)
}
Timber.d("updateSaltPayStatus: success: %s", updateResult)
return Result.Success(updateResult)
}
private suspend fun getAmountToClaimIfNeeded(
saltPayManager: SaltPayRegistrationManager,
amountToClaim: Amount?,
): Amount? {
Timber.d("getAmountToClaimIfNeeded: for amount: %s", amountToClaim)
if (amountToClaim != null) {
Timber.d("getAmountToClaimIfNeeded: checks no need, amount already persist")
return amountToClaim
}
return when (val result = saltPayManager.getAmountToClaim()) {
is Result.Success -> {
Timber.d("getAmountToClaimIfNeeded: success: %s", result.data)
return result.data
}
is Result.Failure -> {
Timber.e(result.error, "getAmountToClaimIfNeeded: failure")
null
}
}
}
private var handleClaimRefreshInProgress: Boolean = false
set(value) {
field = value
store.dispatchOnMain(OnboardingSaltPayAction.SetIsBusy(value))
store.dispatchOnMain(OnboardingSaltPayAction.SetClaimRefreshInProgress(value))
}
private var handleInProgress: Boolean = false
set(value) {
field = value
store.dispatchOnMain(OnboardingSaltPayAction.SetInProgress(value))
}
private suspend fun onError(error: SaltPayRegistrationError) {
withMainContext {
handleIsBusy = false
handleInProgress = false
handleError = error
}
}
private suspend fun onException(error: Throwable) {
withMainContext {
handleIsBusy = false
handleInProgress = false
handleException = error
}
}
@ -221,35 +383,58 @@ private suspend fun checkGasIfNeeded(
saltPayManager: SaltPayRegistrationManager,
step: SaltPayRegistrationStep,
): Result<Unit> {
Timber.d("checkGasIfNeeded: for step: %s", step)
if (step == SaltPayRegistrationStep.KycStart ||
step == SaltPayRegistrationStep.KycWaiting ||
step == SaltPayRegistrationStep.Finished
step == SaltPayRegistrationStep.ClaimSuccess
) {
Timber.d("checkGasIfNeeded: no need to check for step: %s", step)
return Result.Success(Unit)
}
return saltPayManager.checkHasGas()
val result = saltPayManager.checkHasGas()
when (result) {
is Result.Success -> Timber.d("checkGasIfNeeded: has GAS")
is Result.Failure -> Timber.d("checkGasIfNeeded: has NO GAS")
}
return result
}
private suspend fun registerKYCIfNeeded(state: OnboardingSaltPayState): Result<Unit> {
if (state.step != SaltPayRegistrationStep.KycStart) {
private suspend fun registerKYCIfNeeded(
saltPayManager: SaltPayRegistrationManager,
step: SaltPayRegistrationStep,
): Result<Unit> {
Timber.d("registerKYCIfNeeded: step: %s", step)
if (step != SaltPayRegistrationStep.KycStart) {
Timber.d("registerKYCIfNeeded: return Success, because step != KycStart")
return Result.Success(Unit)
}
withMainContext {
Timber.d("registerKYCIfNeeded: set new step: KycWaiting")
store.dispatch(OnboardingSaltPayAction.SetStep(SaltPayRegistrationStep.KycWaiting))
}
return state.saltPayManager.registerKYC()
Timber.d("saltPayManager.registerKYC()")
return saltPayManager.registerKYC()
}
private suspend fun checkRegistration(
state: OnboardingSaltPayState,
saltPayManager: SaltPayRegistrationManager,
): Result<SaltPayRegistrationStep> {
Timber.d("checkRegistration")
return try {
val registrationResponseItem = state.saltPayManager.checkRegistration().successOr { return it }
val registrationResponseItem = saltPayManager.checkRegistration().successOr {
Timber.e(it.error, "checkRegistration: failure")
return it
}
Timber.d("checkRegistration: success: try convert to SaltPayStep")
val step = registrationResponseItem.toSaltPayStep()
Timber.d("checkRegistration: step: %s", step)
Result.Success(step)
} catch (ex: SaltPayRegistrationError) {
Timber.e(ex, "checkRegistration: step")
Result.Failure(ex)
}
}
@ -260,8 +445,8 @@ fun RegistrationResponse.Item.toSaltPayStep(): SaltPayRegistrationStep {
passed != true -> throw SaltPayRegistrationError.CardNotPassed(this.error)
disabledByAdmin == true -> throw SaltPayRegistrationError.CardDisabled(this.error)
// go to success screen
active == true -> SaltPayRegistrationStep.Finished
// go to claim screen
active == true -> SaltPayRegistrationStep.Claim
// pinSet is false, go toPin screen
pinSet == false -> SaltPayRegistrationStep.NeedPin

View file

@ -1,8 +1,6 @@
package com.tangem.tap.features.onboarding.products.wallet.saltPay.redux
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupStep
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletStep
import org.rekotlin.Action
/**
@ -31,20 +29,6 @@ private fun internalReduce(anyAction: Action, onboardingWalletState: OnboardingW
val oldState = onboardingWalletState.onboardingSaltPayState ?: return onboardingWalletState
val newState = when (action) {
OnboardingSaltPayAction.Init.DiscardBackupSteps -> {
return onboardingWalletState.copy(
step = OnboardingWalletStep.SaltPay,
backupState = onboardingWalletState.backupState.copy(
backupStep = BackupStep.Finished,
maxBackupCards = 1,
canSkipBackup = false,
),
onboardingSaltPayState = oldState.copy(
isTest = true,
step = SaltPayRegistrationStep.NeedPin,
),
)
}
is OnboardingSaltPayAction.SetAccessCode -> {
oldState.copy(
accessCode = action.accessCode,
@ -62,19 +46,27 @@ private fun internalReduce(anyAction: Action, onboardingWalletState: OnboardingW
)
}
}
is OnboardingSaltPayAction.SetIsBusy -> {
is OnboardingSaltPayAction.SetAmountToClaim -> {
oldState.copy(
isBusy = action.isBusy,
amountToClaim = action.amount,
)
}
is OnboardingSaltPayAction.SetTokenBalance -> {
oldState.copy(
tokenAmount = oldState.tokenAmount.copy(value = action.balanceValue),
)
}
is OnboardingSaltPayAction.SetInProgress -> {
oldState.copy(
inProgress = action.isInProgress,
)
}
is OnboardingSaltPayAction.SetClaimRefreshInProgress -> {
oldState.copy(
claimInProgress = action.isInProgress,
)
}
else -> oldState
// is OnboardingSaltPayAction.Init.PrepareAndSetDependencies -> oldState
// OnboardingSaltPayAction.OnCreate -> oldState
// is OnboardingSaltPayAction.OnFinishKYC -> oldState
// is OnboardingSaltPayAction.Register -> oldState
// is OnboardingSaltPayAction.TrySetPin -> oldState
// is OnboardingSaltPayAction.Update -> oldState
// is OnboardingSaltPayAction.TryUpdateStep -> oldState
}
return when (newState) {

View file

@ -1,9 +1,12 @@
package com.tangem.tap.features.onboarding.products.wallet.saltPay.redux
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.WalletManagerFactory
import com.tangem.common.extensions.guard
import com.tangem.common.extensions.toHexString
import com.tangem.common.json.MoshiJsonConverter
import com.tangem.domain.common.SaltPayWorkaround
import com.tangem.domain.common.ScanResponse
import com.tangem.network.api.paymentology.PaymentologyApiService
import com.tangem.tap.common.toggleWidget.WidgetState
@ -17,6 +20,7 @@ import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.persistence.SaltPayRegistrationStorage
import com.tangem.tap.preferencesStorage
import com.tangem.tap.store
import java.math.BigDecimal
/**
[REDACTED_AUTHOR]
@ -27,14 +31,18 @@ data class OnboardingSaltPayState(
val saltPayConfig: SaltPayConfig = SaltPayConfig.stub(),
val pinCode: String? = null,
val accessCode: String? = null,
val step: SaltPayRegistrationStep = SaltPayRegistrationStep.NeedPin,
val amountToClaim: Amount? = null,
val tokenAmount: Amount = Amount(SaltPayWorkaround.tokenFrom(Blockchain.SaltPay), BigDecimal.ZERO),
val step: SaltPayRegistrationStep = SaltPayRegistrationStep.None,
val saltPayCardArtworkUrl: String? = null,
val isBusy: Boolean = false,
val isTest: Boolean = false,
val inProgress: Boolean = false,
val claimInProgress: Boolean = false,
) {
val mainButtonState: WidgetState
get() = if (isBusy) ProgressState.Loading else ProgressState.Done
get() = if (inProgress) ProgressState.Loading else ProgressState.Done
fun readyToClaim(): Boolean = amountToClaim != null
val pinLength: Int = 4
@ -108,11 +116,14 @@ data class OnboardingSaltPayState(
}
enum class SaltPayRegistrationStep {
None,
NoGas,
NeedPin,
CardRegistration,
KycIntro,
KycStart,
KycWaiting,
Finished;
Claim,
ClaimInProgress,
ClaimSuccess;
}

View file

@ -2,26 +2,34 @@ package com.tangem.tap.features.onboarding.products.wallet.saltPay.ui
import android.view.View
import android.view.ViewGroup
import android.view.animation.OvershootInterpolator
import android.widget.ProgressBar
import androidx.annotation.LayoutRes
import androidx.annotation.StringRes
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.view.children
import androidx.transition.TransitionManager
import coil.load
import coil.size.Scale
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.button.MaterialButton
import com.tangem.common.extensions.guard
import com.tangem.tap.common.compose.PinCodeWidget
import com.tangem.tap.common.extensions.beginDelayedTransition
import com.tangem.tap.common.extensions.configureSettings
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.getDrawableCompat
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.extensions.stop
import com.tangem.tap.common.extensions.toFormattedCurrencyString
import com.tangem.tap.common.feedback.SupportInfo
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.common.toggleWidget.IndeterminateProgressButtonWidget
import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget
import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupState
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupStep
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletAction
@ -34,8 +42,12 @@ import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.Onboardi
import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.OnboardingSaltPayState
import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.SaltPayRegistrationStep
import com.tangem.tap.features.onboarding.products.wallet.ui.OnboardingWalletFragment
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.store
import com.tangem.wallet.R
import com.tangem.wallet.databinding.LayoutOnboardingContainerBottomBinding
import com.tangem.wallet.databinding.LayoutOnboardingContainerTopBinding
import com.tangem.wallet.databinding.LayoutOnboardingMainBinding
/**
[REDACTED_AUTHOR]
@ -45,6 +57,11 @@ internal class OnboardingSaltPayView(
) {
private val toolbar: MaterialToolbar by lazy { walletFragment.binding.toolbar }
private val claimBinding: LayoutOnboardingMainBinding by lazy { walletFragment.bindingSaltPay.claim }
private val btnRefreshBalanceWidget by lazy {
RefreshBalanceWidget(claimBinding.onboardingTopContainer.onboardingWalletContainer)
}
private var progressButton: SaltPayProgressButton? = null
@ -86,11 +103,6 @@ internal class OnboardingSaltPayView(
// walletFragment.loadImageIntoImageView(state.onboardingSaltPayState.saltPayCardArtworkUrl, imvFrontCard)
// }
// walletFragment.loadImageIntoImageView(state.cardArtworkUrl, imvFirstBackupCard)
//TODO: at now we can hide the image only by changing alpha channel to 0, because
// the OnboardingWalletFragment and WalletCardsWidget manipulate it visibility through changing
// View.VISIBILITY states.
// imvSecondBackupCard.alpha = 0f
}
fun showAddBackupCards(state: BackupState) = with(walletFragment.binding) {
@ -123,10 +135,9 @@ internal class OnboardingSaltPayView(
SaltPayRegistrationStep.KycIntro -> handleKycIntro()
SaltPayRegistrationStep.KycStart -> handleKycStart(state)
SaltPayRegistrationStep.KycWaiting -> handleKycWaiting()
// SaltPayRegistrationStep.Claim -> handleKycWaiting()
// SaltPayRegistrationStep.ClaimInProgress -> handleKycWaiting()
// SaltPayRegistrationStep.ClaimSuccess -> handleKycWaiting()
SaltPayRegistrationStep.Finished -> handleFinished()
SaltPayRegistrationStep.Claim -> handleClaim(state)
SaltPayRegistrationStep.ClaimInProgress -> handleClaim(state)
SaltPayRegistrationStep.ClaimSuccess -> handleClaim(state)
}
progressButton?.changeState(state.mainButtonState)
}
@ -138,7 +149,7 @@ internal class OnboardingSaltPayView(
}
private fun handleNeedPin() = with(walletFragment.bindingSaltPay) {
toolbar.title = walletFragment.getString(R.string.onboarding_navbar_pin)
toolbar.title = getString(R.string.onboarding_navbar_pin)
showOnlyView(pinCode.root) {
progressButton = SaltPayProgressButton(pinCode.root)
progressButton?.mainView?.isEnabled = false
@ -157,7 +168,7 @@ internal class OnboardingSaltPayView(
}
private fun handleCardRegistration() = with(walletFragment.bindingSaltPay) {
toolbar.title = walletFragment.getString(R.string.onboarding_navbar_register_wallet)
toolbar.title = getString(R.string.onboarding_navbar_register_wallet)
showOnlyView(connectCard.root) {
progressButton = SaltPayProgressButton(connectCard.root)
connectCard.btnConnect.setOnClickListener {
@ -167,17 +178,17 @@ internal class OnboardingSaltPayView(
}
private fun handleKycIntro() = with(walletFragment.bindingSaltPay) {
toolbar.title = walletFragment.getString(R.string.onboarding_navbar_kyc_start)
toolbar.title = getString(R.string.onboarding_navbar_kyc_start)
showOnlyView(verifyIdentity.root) {
progressButton = SaltPayProgressButton(verifyIdentity.root)
verifyIdentity.btnVerify.setOnClickListener {
store.dispatch(OnboardingSaltPayAction.KYCStart)
store.dispatch(OnboardingSaltPayAction.OpenUtorgKYC)
}
}
}
private fun handleKycStart(state: OnboardingSaltPayState) = with(walletFragment.bindingSaltPay) {
toolbar.title = walletFragment.getString(R.string.onboarding_navbar_kyc_start)
toolbar.title = getString(R.string.onboarding_navbar_kyc_start)
showOnlyView(webvVerifyIdentity) {
progressButton = null
webvVerifyIdentity.configureSettings()
@ -185,7 +196,7 @@ internal class OnboardingSaltPayView(
successUrl = state.saltPayManager.kycUrlProvider.doneUrl,
onSuccess = {
webvVerifyIdentity.stop()
store.dispatch(OnboardingSaltPayAction.OnFinishKYC)
store.dispatch(OnboardingSaltPayAction.UtorgKYCRedirectSuccess)
},
)
webvVerifyIdentity.webChromeClient = UtorgWebChromeClient(walletFragment.requireActivity())
@ -194,7 +205,7 @@ internal class OnboardingSaltPayView(
}
private fun handleKycWaiting() = with(walletFragment.bindingSaltPay) {
toolbar.title = walletFragment.getString(R.string.onboarding_navbar_kyc_waiting)
toolbar.title = getString(R.string.onboarding_navbar_kyc_waiting)
showOnlyView(verifyIdentityInProgress.root) {
progressButton = SaltPayProgressButton(verifyIdentityInProgress.root)
verifyIdentityInProgress.btnOpenSupportChat.setOnClickListener {
@ -206,56 +217,105 @@ internal class OnboardingSaltPayView(
}
}
// private fun setupTopUpWalletState(state: OnboardingNoteState) = with(binding.onboardingActionContainer) {
// if (state.isBuyAllowed) {
// btnMainAction.setText(com.tangem.wallet.R.string.onboarding_top_up_button_but_crypto)
// btnMainAction.setOnClickListener {
// com.tangem.tap.store.dispatch(com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction.TopUp)
// }
//
// btnAlternativeAction.isVisible = true
// btnAlternativeAction.setText(com.tangem.wallet.R.string.onboarding_top_up_button_show_wallet_address)
// btnAlternativeAction.setOnClickListener {
// com.tangem.tap.store.dispatch(com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction.ShowAddressInfoDialog)
// }
// } else {
// btnMainAction.setText(com.tangem.wallet.R.string.onboarding_button_receive_crypto)
// btnMainAction.setOnClickListener {
// com.tangem.tap.store.dispatch(com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction.ShowAddressInfoDialog)
// }
//
// btnAlternativeAction.isVisible = false
// }
//
// tvHeader.setText(com.tangem.wallet.R.string.onboarding_top_up_header)
// if (state.balanceNonCriticalError == null) {
// tvBody.setText(com.tangem.wallet.R.string.onboarding_top_up_body)
// } else {
// state.walletBalance.amountToCreateAccount?.let { amount ->
// val tvBodyMessage = getString(
// com.tangem.wallet.R.string.onboarding_top_up_body_no_account_error,
// amount, state.walletBalance.currency.currencySymbol,
// )
// tvBody.text = tvBodyMessage
// }
// }
//
// btnRefreshBalanceWidget.changeState(state.walletBalance.state)
// if (btnRefreshBalanceWidget.isShowing != true) {
// btnRefreshBalanceWidget.mainView.setOnClickListener {
// com.tangem.tap.store.dispatch(com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction.Balance.Update)
// }
// }
// binding.onboardingTopContainer.imvCardBackground
// .setBackgroundDrawable(requireContext().getDrawableCompat(com.tangem.wallet.R.drawable.shape_rectangle_rounded_8))
// updateConstraints(state.currentStep, com.tangem.wallet.R.layout.lp_onboarding_topup_wallet)
// }
private val topContainer: LayoutOnboardingContainerTopBinding by lazy { claimBinding.onboardingTopContainer }
private val actionContainer: LayoutOnboardingContainerBottomBinding by lazy { claimBinding.onboardingActionContainer }
private var previousStep: SaltPayRegistrationStep = SaltPayRegistrationStep.None
private fun handleFinished() {
walletFragment.binding.onboardingRoot.beginDelayedTransition()
walletFragment.bindingSaltPay.onboardingSaltpayContainer.hide()
walletFragment.binding.onboardingWalletContainer.show()
walletFragment.showSuccess()
private fun handleClaim(state: OnboardingSaltPayState) = with(walletFragment.bindingSaltPay) {
toolbar.title = getString(R.string.onboarding_navbar_claim)
val btnMain = actionContainer.btnContainer.findViewById<MaterialButton>(R.id.btn_main_action)
val tvHeader = actionContainer.tvHeader
val tvBody = actionContainer.tvBody
showOnlyView(claimBinding.root) {
// topContainer.progress.pbState.hide()
progressButton = SaltPayProgressButton(actionContainer.root)
topContainer.imvCardBackground.apply {
setBackgroundDrawable(context.getDrawableCompat(R.drawable.shape_rectangle_rounded_8))
}
topContainer.imvFrontCard.load(R.drawable.img_salt_pay_visa) {
scale(Scale.FILL)
crossfade(enable = true)
}
actionContainer.btnAlternativeAction.hide()
updateConstraints(state.step, R.layout.lp_onboarding_topup_wallet)
}
btnRefreshBalanceWidget.changeState(if (state.claimInProgress) ProgressState.Loading else ProgressState.Done)
if (btnRefreshBalanceWidget.isShowing != true) {
btnRefreshBalanceWidget.mainView.setOnClickListener {
store.dispatch(OnboardingSaltPayAction.RefreshClaim)
}
}
val tokenAmountString = tokenAmountString(state)
topContainer.onboardingTvBalance.tvBalanceValue.text = tokenAmountString ?: ""
topContainer.onboardingTvBalance.tvBalanceCurrency.text = ""
when (state.step) {
SaltPayRegistrationStep.Claim -> {
btnRefreshBalanceWidget.mainView.hide()
claimValueString(state)?.let {
tvHeader.text = getString(R.string.onboarding_title_claim, it)
}
tvBody.text = getString(R.string.onboarding_subtitle_claim)
btnMain.text = getString(R.string.onboarding_button_claim)
btnMain.setOnClickListener {
store.dispatch(OnboardingSaltPayAction.Claim)
}
}
SaltPayRegistrationStep.ClaimInProgress -> {
btnRefreshBalanceWidget.mainView.show()
tvHeader.text = getString(R.string.onboarding_title_claim_progress)
tvBody.text = getString(R.string.onboarding_subtitle_claim_progress)
btnMain.text = getString(R.string.onboarding_button_claim)
}
SaltPayRegistrationStep.ClaimSuccess -> {
tvHeader.setText(R.string.common_success)
tvBody.setText(R.string.onboarding_subtitle_success_claim)
btnMain.setText(R.string.onboarding_button_continue_wallet)
btnMain.setOnClickListener {
walletFragment.showConfetti(false)
store.dispatch(OnboardingWalletAction.FinishOnboarding)
}
btnRefreshBalanceWidget.mainView.setOnClickListener(null)
updateConstraints(state.step, R.layout.lp_onboarding_done_activation)
walletFragment.showConfetti(true)
}
else -> {}
}
// need to recreate after any step
progressButton = SaltPayProgressButton(actionContainer.root)
}
private fun tokenAmountString(state: OnboardingSaltPayState): String? {
return state.tokenAmount.value?.toFormattedCurrencyString(
decimals = state.tokenAmount.decimals,
currency = state.tokenAmount.currencySymbol,
)
}
private fun claimValueString(state: OnboardingSaltPayState): String? {
return state.amountToClaim?.value?.toFormattedCurrencyString(
decimals = state.amountToClaim.decimals,
currency = state.amountToClaim.currencySymbol,
)
}
private fun updateConstraints(currentStep: SaltPayRegistrationStep, @LayoutRes layoutId: Int) {
if (this.previousStep == currentStep) return
with(claimBinding.onboardingTopContainer) {
val constraintSet = ConstraintSet()
constraintSet.clone(root.context, layoutId)
constraintSet.applyTo(onboardingWalletContainer)
val transition = InternalNoteLayoutTransition()
transition.interpolator = OvershootInterpolator()
TransitionManager.beginDelayedTransition(onboardingWalletContainer, transition)
}
}
private fun showOnlyView(view: View, onShowListener: (() -> Unit)? = null) {
@ -265,6 +325,14 @@ internal class OnboardingSaltPayView(
view.show { onShowListener?.invoke() }
}
private fun getString(@StringRes resId: Int): String {
return walletFragment.getString(resId)
}
private fun getString(@StringRes resId: Int, vararg formatArgs: Any?): String {
return walletFragment.getString(resId, *formatArgs)
}
private class SaltPayProgressButton(
root: ViewGroup,
) : IndeterminateProgressButtonWidget(findButton(root), findProgress(root)) {

View file

@ -169,9 +169,9 @@ class OnboardingWalletFragment : BaseFragment(R.layout.fragment_onboarding_walle
}
private fun setupCreateWalletState() = with(binding) {
layoutButtonsCommon.btnMainAction.setText(R.string.onboarding_create_wallet_button_create_wallet)
layoutButtonsCommon.btnMainAction.setOnClickListener { store.dispatch(OnboardingWalletAction.CreateWallet) }
layoutButtonsCommon.btnAlternativeAction.hide()
layoutButtonsCommon.btnWalletMainAction.setText(R.string.onboarding_create_wallet_button_create_wallet)
layoutButtonsCommon.btnWalletMainAction.setOnClickListener { store.dispatch(OnboardingWalletAction.CreateWallet) }
layoutButtonsCommon.btnWalletAlternativeAction.hide()
toolbar.title = getText(R.string.onboarding_getting_started)
@ -205,12 +205,12 @@ class OnboardingWalletFragment : BaseFragment(R.layout.fragment_onboarding_walle
tabLayoutBackupInfo.show()
with(layoutButtonsCommon) {
btnMainAction.text = getText(R.string.onboarding_button_backup_now)
btnMainAction.setOnClickListener { store.dispatch(BackupAction.StartBackup) }
btnWalletMainAction.text = getText(R.string.onboarding_button_backup_now)
btnWalletMainAction.setOnClickListener { store.dispatch(BackupAction.StartBackup) }
btnAlternativeAction.text = getText(R.string.onboarding_button_skip_backup)
btnAlternativeAction.setOnClickListener { store.dispatch(BackupAction.DismissBackup) }
btnAlternativeAction.show(state.canSkipBackup && !isSaltPay)
btnWalletAlternativeAction.text = getText(R.string.onboarding_button_skip_backup)
btnWalletAlternativeAction.setOnClickListener { store.dispatch(BackupAction.DismissBackup) }
btnWalletAlternativeAction.show(state.canSkipBackup && !isSaltPay)
}
animator.showBackupIntro(state)
}
@ -223,9 +223,9 @@ class OnboardingWalletFragment : BaseFragment(R.layout.fragment_onboarding_walle
)
with(layoutButtonsCommon) {
btnMainAction.text = getString(R.string.onboarding_button_scan_origin_card)
btnAlternativeAction.hide()
btnMainAction.setOnClickListener { store.dispatch(BackupAction.ScanPrimaryCard) }
btnWalletMainAction.text = getString(R.string.onboarding_button_scan_origin_card)
btnWalletAlternativeAction.hide()
btnWalletMainAction.setOnClickListener { store.dispatch(BackupAction.ScanPrimaryCard) }
}
animator.showScanOriginCard()
@ -323,16 +323,16 @@ class OnboardingWalletFragment : BaseFragment(R.layout.fragment_onboarding_walle
if (isSaltPay) {
tvHeader.text = getText(R.string.onboarding_saltpay_title_prepare_origin)
tvBody.text = getString(R.string.onboarding_twins_interrupt_warning)
layoutButtonsCommon.btnMainAction.text = getText(R.string.onboarding_saltpay_button_backup_origin)
layoutButtonsCommon.btnWalletMainAction.text = getText(R.string.onboarding_saltpay_button_backup_origin)
} else {
tvHeader.text = getText(R.string.onboarding_title_prepare_origin)
tvBody.text = getString(
R.string.onboarding_subtitle_scan_primary_card_format,
state.primaryCardId?.let { cardIdFormatter.getFormattedCardId(it) },
)
layoutButtonsCommon.btnMainAction.text = getText(R.string.onboarding_button_backup_origin)
layoutButtonsCommon.btnWalletMainAction.text = getText(R.string.onboarding_button_backup_origin)
}
layoutButtonsCommon.btnMainAction.setOnClickListener { store.dispatch(BackupAction.WritePrimaryCard) }
layoutButtonsCommon.btnWalletMainAction.setOnClickListener { store.dispatch(BackupAction.WritePrimaryCard) }
animator.showWritePrimaryCard(state)
}
@ -342,7 +342,7 @@ class OnboardingWalletFragment : BaseFragment(R.layout.fragment_onboarding_walle
layoutButtonsAddCards.root.hide()
layoutButtonsCommon.root.show()
layoutButtonsCommon.btnAlternativeAction.hide()
layoutButtonsCommon.btnWalletAlternativeAction.hide()
imvCardBackground.hide()
imvFirstBackupCard.show()
@ -356,7 +356,7 @@ class OnboardingWalletFragment : BaseFragment(R.layout.fragment_onboarding_walle
if (isSaltPay) {
tvHeader.text = getString(R.string.onboarding_saltpay_title_backup_card)
tvBody.text = getString(R.string.onboarding_twins_interrupt_warning)
layoutButtonsCommon.btnMainAction.text = getString(R.string.onboarding_saltpay_title_backup_card)
layoutButtonsCommon.btnWalletMainAction.text = getString(R.string.onboarding_saltpay_title_backup_card)
} else {
val cardIdFormatter = CardIdFormatter(CardIdDisplayFormat.LastMasked(4))
tvHeader.text = getString(R.string.onboarding_title_backup_card_format, cardNumber)
@ -364,12 +364,12 @@ class OnboardingWalletFragment : BaseFragment(R.layout.fragment_onboarding_walle
R.string.onboarding_subtitle_scan_backup_card_format,
cardIdFormatter.getFormattedCardId(state.backupCardIds[cardNumber - 1]),
)
layoutButtonsCommon.btnMainAction.text = getString(
layoutButtonsCommon.btnWalletMainAction.text = getString(
R.string.onboarding_button_backup_card_format,
cardNumber,
)
}
layoutButtonsCommon.btnMainAction.setOnClickListener {
layoutButtonsCommon.btnWalletMainAction.setOnClickListener {
store.dispatch(BackupAction.WriteBackupCard(cardNumber))
}
@ -386,25 +386,33 @@ class OnboardingWalletFragment : BaseFragment(R.layout.fragment_onboarding_walle
tabLayoutBackupInfo.hide()
tvBody.text = getText(R.string.onboarding_subtitle_success_tangem_wallet_onboarding)
layoutButtonsCommon.btnMainAction.text = getText(R.string.onboarding_button_continue_wallet)
layoutButtonsCommon.btnAlternativeAction.hide()
layoutButtonsCommon.btnMainAction.setOnClickListener {
vConfetti.lavConfetti.cancelAnimation()
vConfetti.lavConfetti.hide()
layoutButtonsCommon.btnWalletMainAction.text = getText(R.string.onboarding_button_continue_wallet)
layoutButtonsCommon.btnWalletAlternativeAction.hide()
layoutButtonsCommon.btnWalletMainAction.setOnClickListener {
showConfetti(false)
store.dispatch(OnboardingWalletAction.FinishOnboarding)
}
animator.showSuccess {
flCardsContainer.hide()
imvCardBackground.hide()
vConfetti.lavConfetti.show()
vConfetti.lavConfetti.playAnimation()
showConfetti(true)
imvSuccess.alpha = 0f
imvSuccess.show()
imvSuccess.animate()?.alpha(1f)?.duration = 400
}
}
internal fun showConfetti(show: Boolean) = with(binding.vConfetti) {
lavConfetti.show(show)
if (show) {
lavConfetti.playAnimation()
} else {
lavConfetti.cancelAnimation()
}
}
override fun handleOnBackPressed() {
store.dispatch(OnboardingWalletAction.OnBackPressed)
}

View file

@ -13,7 +13,7 @@
android:layout_marginBottom="16dp"
android:gravity="center"
android:orientation="vertical"
app:layout_constraintBottom_toTopOf="@+id/fl_scan_card_container"
app:layout_constraintBottom_toTopOf="@+id/btn_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
@ -39,7 +39,7 @@
android:layout_marginTop="10dp"
android:layout_marginEnd="64dp"
android:minLines="3"
app:layout_constraintBottom_toTopOf="@+id/fl_scan_card_container"
app:layout_constraintBottom_toTopOf="@+id/btn_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_header"
@ -55,14 +55,14 @@
android:textStyle="bold"
android:visibility="gone"
app:buttonTint="@color/accent"
app:layout_constraintBottom_toTopOf="@+id/fl_scan_card_container"
app:layout_constraintBottom_toTopOf="@+id/btn_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearLayoutCompat"
tools:visibility="visible" />
<FrameLayout
android:id="@+id/fl_scan_card_container"
android:id="@+id/btn_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="90dp"
@ -75,7 +75,7 @@
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_main_action"
style="@style/OnboardingButton"
android:text="@string/home_button_scan" />
tools:text="@string/home_button_scan" />
<ProgressBar
android:id="@+id/progress"

View file

@ -6,7 +6,7 @@
android:layout_height="wrap_content">
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_main_action"
android:id="@+id/btn_wallet_main_action"
style="@style/OnboardingButton"
android:layout_width="204dp"
android:layout_height="wrap_content"
@ -17,7 +17,7 @@
tools:text="Main Action" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_alternative_action"
android:id="@+id/btn_wallet_alternative_action"
style="@style/OnboardingButton.Borderless"
android:layout_width="204dp"
android:layout_height="wrap_content"

View file

@ -13,7 +13,7 @@
android:layout_marginBottom="@dimen/home_bottom_llCompat_margin_bottom"
android:gravity="center"
android:orientation="vertical"
app:layout_constraintBottom_toTopOf="@+id/fl_scan_card_container"
app:layout_constraintBottom_toTopOf="@+id/btn_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
@ -39,7 +39,7 @@
android:layout_marginTop="@dimen/home_bottom_tv_body_margin_top"
android:layout_marginEnd="@dimen/home_bottom_tv_body_margin_end"
android:minLines="3"
app:layout_constraintBottom_toTopOf="@+id/fl_scan_card_container"
app:layout_constraintBottom_toTopOf="@+id/btn_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_header"
@ -55,14 +55,14 @@
android:textStyle="bold"
android:visibility="gone"
app:buttonTint="@color/accent"
app:layout_constraintBottom_toTopOf="@+id/fl_scan_card_container"
app:layout_constraintBottom_toTopOf="@+id/btn_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearLayoutCompat"
tools:visibility="visible" />
<FrameLayout
android:id="@+id/fl_scan_card_container"
android:id="@+id/btn_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/home_bottom_fl_scan_card_container_margin_bottom"
@ -75,7 +75,7 @@
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_main_action"
style="@style/OnboardingButton"
android:text="@string/home_button_scan" />
tools:text="@string/home_button_scan" />
<ProgressBar
android:id="@+id/progress"

View file

@ -32,4 +32,9 @@
layout="@layout/layout_onboarding_saltpay_verify_identity_in_progress"
android:visibility="gone" />
<include
android:id="@+id/claim"
layout="@layout/layout_onboarding_main"
android:visibility="gone"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -142,4 +142,11 @@
<string name="onboarding_saltpay_button_backup_origin">Tap the SaltPay card</string>
<string name="onboarding_saltpay_title_backup_card">Tap the Tangem card</string>
<string name="onboarding_chat_button_title">Support</string>
<string name="onboarding_navbar_claim">Getting started</string>
<string name="onboarding_title_claim">Claim %s</string>
<string name="onboarding_subtitle_claim">To get started, simply claim wxDAI to your wallet</string>
<string name="onboarding_button_claim">Claim</string>
<string name="onboarding_subtitle_success_claim">Congratulations! Your first payment crypto card has been activated</string>
<string name="onboarding_title_claim_progress">Claiming</string>
<string name="onboarding_subtitle_claim_progress">It will take a few seconds</string>
</resources>

View file

@ -142,4 +142,11 @@
<string name="onboarding_saltpay_button_backup_origin">Tap the SaltPay card</string>
<string name="onboarding_saltpay_title_backup_card">Tap the Tangem card</string>
<string name="onboarding_chat_button_title">Support</string>
<string name="onboarding_navbar_claim">Getting started</string>
<string name="onboarding_title_claim">Claim %s</string>
<string name="onboarding_subtitle_claim">To get started, simply claim wxDAI to your wallet</string>
<string name="onboarding_button_claim">Claim</string>
<string name="onboarding_subtitle_success_claim">Congratulations! Your first payment crypto card has been activated</string>
<string name="onboarding_title_claim_progress">Claiming</string>
<string name="onboarding_subtitle_claim_progress">It will take a few seconds</string>
</resources>

View file

@ -142,4 +142,11 @@
<string name="onboarding_saltpay_button_backup_origin">Tap the SaltPay card</string>
<string name="onboarding_saltpay_title_backup_card">Tap the Tangem card</string>
<string name="onboarding_chat_button_title">Support</string>
<string name="onboarding_navbar_claim">Getting started</string>
<string name="onboarding_title_claim">Claim %s</string>
<string name="onboarding_subtitle_claim">To get started, simply claim wxDAI to your wallet</string>
<string name="onboarding_button_claim">Claim</string>
<string name="onboarding_subtitle_success_claim">Congratulations! Your first payment crypto card has been activated</string>
<string name="onboarding_title_claim_progress">Claiming</string>
<string name="onboarding_subtitle_claim_progress">It will take a few seconds</string>
</resources>

View file

@ -140,4 +140,11 @@
<string name="onboarding_saltpay_button_backup_origin">Приложите SaltPay карту</string>
<string name="onboarding_saltpay_title_backup_card">Приложите Tangem карту</string>
<string name="onboarding_chat_button_title">Чат</string>
<string name="onboarding_navbar_claim">Начало работы</string>
<string name="onboarding_title_claim">Запросить %s</string>
<string name="onboarding_subtitle_claim">Для начала работы просто запросите начисление xDai на свой кошелек</string>
<string name="onboarding_button_claim">Запросить</string>
<string name="onboarding_subtitle_success_claim">Поздравляем! Ваша платежная крипто карта теперь активирована!</string>
<string name="onboarding_title_claim_progress">Запрашивается</string>
<string name="onboarding_subtitle_claim_progress">Это займет несколько секунд</string>
</resources>

View file

@ -142,4 +142,11 @@
<string name="onboarding_saltpay_button_backup_origin">Tap the SaltPay card</string>
<string name="onboarding_saltpay_title_backup_card">Tap the Tangem card</string>
<string name="onboarding_chat_button_title">Support</string>
<string name="onboarding_navbar_claim">Getting started</string>
<string name="onboarding_title_claim">Claim %s</string>
<string name="onboarding_subtitle_claim">To get started, simply claim wxDAI to your wallet</string>
<string name="onboarding_button_claim">Claim</string>
<string name="onboarding_subtitle_success_claim">Congratulations! Your first payment crypto card has been activated</string>
<string name="onboarding_title_claim_progress">Claiming</string>
<string name="onboarding_subtitle_claim_progress">It will take a few seconds</string>
</resources>

View file

@ -3,7 +3,7 @@ ext.versions = [
build_gradle : '7.1.3',
tangem_card_sdk : 'develop-165',
// tangem_card_sdk : '0.0.1',
tangem_blockchain_sdk: 'develop-129',
tangem_blockchain_sdk: 'develop-130',
// tangem_blockchain_sdk: '0.0.1',
]