Updated on 2026-08-14
This commit is contained in:
parent
1182364325
commit
7e821a934c
34 changed files with 443 additions and 615 deletions
|
|
@ -75,7 +75,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler {
|
|||
|
||||
val backStackIsEmpty = supportFragmentManager.backStackEntryCount == 0
|
||||
val isScannedBefore = store.state.globalState.scanResponse != null
|
||||
val isOnboardingServiceActive = store.state.globalState.onboardingService != null
|
||||
val isOnboardingServiceActive = store.state.globalState.onboardingManager != null
|
||||
if (backStackIsEmpty || (!isOnboardingServiceActive && !isScannedBefore)) {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Home))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import androidx.transition.Transition
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
inline fun Transition.addListener(
|
||||
crossinline onStart: (animator: Transition) -> Unit = {},
|
||||
crossinline onEnd: (animator: Transition) -> Unit = {},
|
||||
crossinline onCancel: (animator: Transition) -> Unit = {},
|
||||
crossinline onPause: (animator: Transition) -> Unit = {},
|
||||
crossinline onRepeat: (animator: Transition) -> Unit = {}
|
||||
): Transition.TransitionListener {
|
||||
val listener = object : Transition.TransitionListener {
|
||||
override fun onTransitionStart(transition: Transition) = onStart(transition)
|
||||
override fun onTransitionEnd(transition: Transition) = onEnd(transition)
|
||||
override fun onTransitionCancel(transition: Transition) = onCancel(transition)
|
||||
override fun onTransitionPause(transition: Transition) = onPause(transition)
|
||||
override fun onTransitionResume(transition: Transition) = onRepeat(transition)
|
||||
}
|
||||
addListener(listener)
|
||||
return listener
|
||||
}
|
||||
|
|
@ -7,7 +7,11 @@ import com.tangem.tap.domain.TapError
|
|||
import com.tangem.tap.domain.extensions.amountToCreateAccount
|
||||
import com.tangem.tap.domain.extensions.isNoAccountError
|
||||
import com.tangem.tap.domain.getFirstToken
|
||||
import com.tangem.tap.domain.topup.TradeCryptoHelper
|
||||
import com.tangem.tap.features.wallet.redux.AddressData
|
||||
import com.tangem.tap.features.wallet.redux.reducers.createAddressesData
|
||||
import com.tangem.tap.network.NetworkConnectivity
|
||||
import com.tangem.tap.store
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -29,4 +33,27 @@ suspend fun WalletManager.safeUpdate(): Result<Wallet> = try {
|
|||
Result.Failure(TapError.WalletManagerUpdate.InternalError(message))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun WalletManager?.getToUpUrl(): String? {
|
||||
val wallet = this?.wallet ?: return null
|
||||
val config = store.state.globalState.configManager?.config ?: return null
|
||||
val defaultAddress = wallet.address
|
||||
|
||||
return TradeCryptoHelper.getUrl(
|
||||
TradeCryptoHelper.Action.Buy,
|
||||
wallet.blockchain,
|
||||
wallet.blockchain.currency,
|
||||
defaultAddress,
|
||||
config.moonPayApiKey,
|
||||
config.moonPayApiSecretKey
|
||||
)
|
||||
}
|
||||
|
||||
fun WalletManager?.getAddressData(): AddressData? {
|
||||
val wallet = this?.wallet ?: return null
|
||||
|
||||
val addressDataList = wallet.createAddressesData()
|
||||
return if (addressDataList.isEmpty()) null
|
||||
else addressDataList[0]
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import com.tangem.blockchain.common.WalletManager
|
|||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.tap.common.redux.*
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.configurable.config.ConfigManager
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
|
||||
|
|
@ -12,7 +13,6 @@ import com.tangem.tap.domain.tasks.product.ScanResponse
|
|||
import com.tangem.tap.features.details.redux.SecurityOption
|
||||
import com.tangem.tap.features.feedback.EmailData
|
||||
import com.tangem.tap.features.feedback.FeedbackManager
|
||||
import com.tangem.tap.features.onboarding.service.ProductOnboardingService
|
||||
import com.tangem.tap.network.moonpay.MoonPayUserStatus
|
||||
import org.rekotlin.Action
|
||||
|
||||
|
|
@ -29,11 +29,11 @@ sealed class GlobalAction : Action {
|
|||
object HideDialog : GlobalAction()
|
||||
|
||||
sealed class Onboarding {
|
||||
data class Activate(val onboardingService: ProductOnboardingService) : GlobalAction()
|
||||
object Deactivate : GlobalAction()
|
||||
data class Start(val scanResponse: ScanResponse, val fromScreen: AppScreen) : GlobalAction()
|
||||
object Stop : GlobalAction()
|
||||
}
|
||||
|
||||
data class ReadCard(
|
||||
data class ScanCard(
|
||||
val onSuccess: ((ScanResponse) -> Unit)? = null,
|
||||
val onFailure: ((TangemError) -> Unit)? = null,
|
||||
val messageResId: Int? = null,
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ private val globalMiddlewareHandler: Middleware<AppState> = { dispatch, appState
|
|||
}
|
||||
}
|
||||
}
|
||||
is GlobalAction.ReadCard -> {
|
||||
is GlobalAction.ScanCard -> {
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.scanProduct(FirebaseAnalyticsHandler, action.messageResId)
|
||||
store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.tap.common.redux.global
|
||||
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.features.onboarding.OnboardingManager
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import org.rekotlin.Action
|
||||
|
||||
fun globalReducer(action: Action, state: AppState): GlobalState {
|
||||
|
|
@ -10,11 +12,13 @@ fun globalReducer(action: Action, state: AppState): GlobalState {
|
|||
val globalState = state.globalState
|
||||
|
||||
return when (action) {
|
||||
is GlobalAction.Onboarding.Activate -> {
|
||||
globalState.copy(onboardingService = action.onboardingService)
|
||||
is GlobalAction.Onboarding.Start -> {
|
||||
val usedCardsPrefStorage = preferencesStorage.usedCardsPrefStorage
|
||||
val onboardingState = OnboardingManager(action.scanResponse, action.fromScreen, usedCardsPrefStorage)
|
||||
globalState.copy(onboardingManager = onboardingState)
|
||||
}
|
||||
is GlobalAction.Onboarding.Deactivate -> {
|
||||
globalState.copy(onboardingService = null)
|
||||
is GlobalAction.Onboarding.Stop -> {
|
||||
globalState.copy(onboardingManager = null)
|
||||
}
|
||||
is GlobalAction.ScanFailsCounter.Increment -> {
|
||||
globalState.copy(scanCardFailsCounter = globalState.scanCardFailsCounter + 1)
|
||||
|
|
@ -36,11 +40,11 @@ fun globalReducer(action: Action, state: AppState): GlobalState {
|
|||
is GlobalAction.SetWarningManager -> globalState.copy(warningManager = action.warningManager)
|
||||
is GlobalAction.UpdateWalletSignedHashes -> {
|
||||
val wallet = globalState.scanResponse?.card
|
||||
?.wallet(action.walletPublicKey)
|
||||
?.copy(
|
||||
totalSignedHashes = action.walletSignedHashes,
|
||||
remainingSignatures = action.remainingSignatures
|
||||
)
|
||||
?.wallet(action.walletPublicKey)
|
||||
?.copy(
|
||||
totalSignedHashes = action.walletSignedHashes,
|
||||
remainingSignatures = action.remainingSignatures
|
||||
)
|
||||
val card = globalState.scanResponse?.card
|
||||
wallet?.let { globalState.scanResponse.card.updateWallet(wallet) }
|
||||
|
||||
|
|
|
|||
|
|
@ -8,13 +8,14 @@ import com.tangem.tap.domain.configurable.config.ConfigManager
|
|||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.features.feedback.FeedbackManager
|
||||
import com.tangem.tap.features.onboarding.service.ProductOnboardingService
|
||||
import com.tangem.tap.features.onboarding.OnboardingManager
|
||||
import com.tangem.tap.network.coinmarketcap.CoinMarketCapService
|
||||
import com.tangem.tap.network.moonpay.MoonPayUserStatus
|
||||
import org.rekotlin.StateType
|
||||
|
||||
data class GlobalState(
|
||||
val scanResponse: ScanResponse? = null,
|
||||
val onboardingManager: OnboardingManager? = null,
|
||||
val cardVerifiedOnline: Boolean = false,
|
||||
val tapWalletManager: TapWalletManager = TapWalletManager(),
|
||||
val payIdManager: PayIdManager = PayIdManager(),
|
||||
|
|
@ -26,7 +27,6 @@ data class GlobalState(
|
|||
val scanCardFailsCounter: Int = 0,
|
||||
val dialog: StateDialog? = null,
|
||||
val moonPayUserStatus: MoonPayUserStatus? = null,
|
||||
val onboardingService: ProductOnboardingService? = null,
|
||||
) : StateType
|
||||
|
||||
typealias CryptoCurrencyName = String
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import com.tangem.tap.domain.extensions.*
|
|||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.domain.tokens.CardCurrencies
|
||||
import com.tangem.tap.domain.twins.getTwinCardNumber
|
||||
import com.tangem.tap.domain.twins.getTwinsCardId
|
||||
import com.tangem.tap.domain.twins.isTangemTwin
|
||||
import com.tangem.tap.features.tokens.redux.TokensAction
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
|
|
@ -91,12 +90,8 @@ class TapWalletManager {
|
|||
store.dispatch(WalletAction.SetIfTestnetCard(data.card.isTestCard))
|
||||
store.dispatch(WalletAction.MultiWallet.SetIsMultiwalletAllowed(data.card.isMultiwalletAllowed))
|
||||
if (data.card.isTangemTwin()) {
|
||||
val secondCardId = data.card.getTwinsCardId()
|
||||
val cardNumber = data.card.getTwinCardNumber()
|
||||
if (secondCardId != null && cardNumber != null) {
|
||||
store.dispatch(WalletAction.TwinsAction.SetTwinCard(
|
||||
secondCardId, cardNumber, isCreatingTwinCardsAllowed = true
|
||||
))
|
||||
data.card.getTwinCardNumber()?.let {
|
||||
store.dispatch(WalletAction.TwinsAction.SetTwinCard(null, it, true))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,11 @@ package com.tangem.tap.domain.extensions
|
|||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.card.CardWallet
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.operations.attestation.CardVerifyAndGetInfo
|
||||
import com.tangem.operations.attestation.OnlineCardVerifier
|
||||
import com.tangem.tap.domain.twins.TwinCardNumber
|
||||
import com.tangem.tap.domain.twins.getTwinCardNumber
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
|
||||
fun Card.getSingleWallet(): CardWallet? {
|
||||
|
|
@ -24,6 +28,35 @@ fun Card.signedHashesCount(): Int {
|
|||
return wallets.map { it.totalSignedHashes ?: 0 }.sum()
|
||||
}
|
||||
|
||||
suspend fun Card.getOrLoadCardArtworkUrl(cardInfo: Result<CardVerifyAndGetInfo.Response.Item>? = null): String {
|
||||
fun ifAnyError(): String {
|
||||
return when {
|
||||
cardId.startsWith(Artwork.SERGIO_CARD_ID) -> Artwork.SERGIO_CARD_URL
|
||||
cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL
|
||||
else -> {
|
||||
when (getTwinCardNumber()) {
|
||||
TwinCardNumber.First -> Artwork.TWIN_CARD_1
|
||||
TwinCardNumber.Second -> Artwork.TWIN_CARD_2
|
||||
else -> Artwork.DEFAULT_IMG_URL
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val cardInfoResult = cardInfo ?: OnlineCardVerifier().getCardInfo(cardId, cardPublicKey)
|
||||
return when (cardInfoResult) {
|
||||
is Result.Success -> {
|
||||
val artworkId = cardInfoResult.data.artwork?.id
|
||||
if (artworkId == null || artworkId.isEmpty()) {
|
||||
ifAnyError()
|
||||
} else {
|
||||
OnlineCardVerifier.getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId)
|
||||
}
|
||||
}
|
||||
is Result.Failure -> ifAnyError()
|
||||
}
|
||||
}
|
||||
|
||||
fun Card.getArtworkUrl(artworkId: String?): String? {
|
||||
return when {
|
||||
artworkId != null -> {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ import com.tangem.tap.tangemSdkManager
|
|||
class TwinCardsManager(private val scanResponse: ScanResponse, context: Context) {
|
||||
|
||||
private val currentCardId: String = scanResponse.card.cardId
|
||||
private val secondCardId: String? = TwinsHelper.getTwinsCardId(currentCardId)
|
||||
// private val secondCardId: String? = TwinsHelper.getTwinsCardId(currentCardId)
|
||||
private val secondCardId: String? = null
|
||||
|
||||
private var currentCardPublicKey: String? = null
|
||||
private var secondCardPublicKey: String? = null
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.tap.domain.twins
|
||||
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.tap.common.extensions.isEven
|
||||
|
||||
class TwinsHelper {
|
||||
companion object {
|
||||
|
|
@ -21,22 +20,6 @@ class TwinsHelper {
|
|||
}
|
||||
}
|
||||
|
||||
fun getTwinsCardId(cardId: String): String? {
|
||||
val cardIdWithNewSeries = when (getTwinCardNumber(cardId) ?: return null) {
|
||||
TwinCardNumber.First -> {
|
||||
val index = if (cardId.startsWith(firstCardSeries[0])) 0 else 1
|
||||
cardId.replace(firstCardSeries[index], secondCardSeries[index])
|
||||
}
|
||||
TwinCardNumber.Second -> {
|
||||
val index = if (cardId.startsWith(secondCardSeries[0])) 0 else 1
|
||||
cardId.replace(secondCardSeries[index], firstCardSeries[index])
|
||||
}
|
||||
}
|
||||
val cardIdWithoutChecksum = cardIdWithNewSeries.dropLast(1)
|
||||
val checkSum = cardIdWithoutChecksum.calculateLuhn()
|
||||
return cardIdWithoutChecksum + checkSum
|
||||
}
|
||||
|
||||
fun getTwinCardIdForUser(cardId: String): String {
|
||||
if (cardId.length < 16) return cardId
|
||||
|
||||
|
|
@ -50,21 +33,6 @@ class TwinsHelper {
|
|||
}
|
||||
}
|
||||
|
||||
private fun String.calculateLuhn(): Int {
|
||||
val checksum = this.reversed()
|
||||
.mapIndexed { index, c ->
|
||||
val digit = if (c in '0'..'9') c - '0' else c - 'A'
|
||||
if (!index.isEven()) {
|
||||
digit
|
||||
} else {
|
||||
val newDigit = digit * 2
|
||||
if (newDigit >= 10) newDigit - 9 else newDigit
|
||||
}
|
||||
}.sum()
|
||||
.rem(10)
|
||||
return (10 - checksum) % 10
|
||||
}
|
||||
|
||||
enum class TwinCardNumber(val number: Int) {
|
||||
First(1), Second(2);
|
||||
|
||||
|
|
@ -82,10 +50,6 @@ fun Card.getTwinCardNumber(): TwinCardNumber? {
|
|||
return TwinsHelper.getTwinCardNumber(this.cardId)
|
||||
}
|
||||
|
||||
fun Card.getTwinsCardId(): String? {
|
||||
return TwinsHelper.getTwinsCardId(this.cardId)
|
||||
}
|
||||
|
||||
fun Card.getTwinCardIdForUser(): String {
|
||||
return TwinsHelper.getTwinCardIdForUser(this.cardId)
|
||||
}
|
||||
|
|
@ -1,24 +1,19 @@
|
|||
package com.tangem.tap.features.home.redux
|
||||
|
||||
import com.tangem.tap.common.entities.IndeterminateProgressButton
|
||||
import com.tangem.tap.common.extensions.dispatchErrorNotification
|
||||
import com.tangem.tap.common.extensions.dispatchOpenUrl
|
||||
import com.tangem.tap.common.extensions.withMainContext
|
||||
import com.tangem.tap.common.post
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.FragmentShareTransition
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE
|
||||
import com.tangem.tap.domain.TapWalletManager
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.features.onboarding.service.OnboardingHelper
|
||||
import com.tangem.tap.features.onboarding.OnboardingHelper
|
||||
import com.tangem.tap.features.send.redux.states.ButtonState
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Middleware
|
||||
|
||||
|
|
@ -52,55 +47,30 @@ private fun handleReadCard() {
|
|||
if (!preferencesStorage.wasDisclaimerAccepted()) {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer))
|
||||
} else {
|
||||
fun changeButtonState(state: ButtonState) {
|
||||
val btnState = IndeterminateProgressButton(state)
|
||||
store.dispatch(HomeAction.ChangeScanCardButtonState(btnState))
|
||||
}
|
||||
|
||||
fun proceedToWalletScreen(tapWalletManager: TapWalletManager, scanResponse: ScanResponse) {
|
||||
scope.launch {
|
||||
tapWalletManager.onCardScanned(scanResponse)
|
||||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
withMainContext { changeButtonState(ButtonState.ENABLED) }
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
}
|
||||
}
|
||||
changeButtonState(ButtonState.PROGRESS)
|
||||
|
||||
store.dispatch(GlobalAction.ReadCard({ scanResponse ->
|
||||
val tapWalletManager = store.state.globalState.tapWalletManager
|
||||
if (!OnboardingHelper.isOnboardingCase(scanResponse)) {
|
||||
proceedToWalletScreen(tapWalletManager, scanResponse)
|
||||
return@ReadCard
|
||||
store.dispatch(GlobalAction.ScanCard({ scanResponse ->
|
||||
if (OnboardingHelper.isOnboardingCase(scanResponse)) {
|
||||
val navigateTo = OnboardingHelper.whereToNavigate(scanResponse)
|
||||
store.dispatch(GlobalAction.Onboarding.Start(scanResponse, AppScreen.Home))
|
||||
navigateTo(navigateTo, store.state.homeState.shareTransition)
|
||||
} else {
|
||||
scope.launch { store.state.globalState.tapWalletManager.onCardScanned(scanResponse) }
|
||||
navigateTo(AppScreen.Wallet, null)
|
||||
}
|
||||
|
||||
val service = OnboardingHelper.createService(
|
||||
AppScreen.Home,
|
||||
scanResponse,
|
||||
tapWalletManager.walletManagerFactory
|
||||
)
|
||||
if (service == null) {
|
||||
proceedToWalletScreen(tapWalletManager, scanResponse)
|
||||
return@ReadCard
|
||||
}
|
||||
|
||||
service.onInitializationProgress = {
|
||||
when (it) {
|
||||
ProgressState.Loading -> changeButtonState(ButtonState.PROGRESS)
|
||||
ProgressState.Done -> changeButtonState(ButtonState.ENABLED)
|
||||
ProgressState.Error -> changeButtonState(ButtonState.ENABLED)
|
||||
}
|
||||
}
|
||||
service.onInitialized = {
|
||||
store.dispatch(GlobalAction.Onboarding.Activate(service))
|
||||
store.dispatch(NavigationAction.NavigateTo(it, store.state.homeState.shareTransition))
|
||||
}
|
||||
service.onError = {
|
||||
store.dispatchErrorNotification(it)
|
||||
}
|
||||
post(DELAY_SDK_DIALOG_CLOSE) { service.initializeOnboarding() }
|
||||
}, {
|
||||
changeButtonState(ButtonState.ENABLED)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeButtonState(state: ButtonState) {
|
||||
val btnState = IndeterminateProgressButton(state)
|
||||
store.dispatch(HomeAction.ChangeScanCardButtonState(btnState))
|
||||
}
|
||||
|
||||
private fun navigateTo(screen: AppScreen, transition: FragmentShareTransition?) {
|
||||
post(DELAY_SDK_DIALOG_CLOSE) {
|
||||
changeButtonState(ButtonState.ENABLED)
|
||||
store.dispatch(NavigationAction.NavigateTo(screen, transition))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.tangem.tap.features.onboarding
|
||||
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.domain.ProductType
|
||||
import com.tangem.tap.domain.extensions.hasWallets
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.preferencesStorage
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class OnboardingHelper {
|
||||
companion object {
|
||||
|
||||
fun isOnboardingCase(response: ScanResponse): Boolean = if (response.card.hasWallets())
|
||||
preferencesStorage.usedCardsPrefStorage.activationIsStarted(response.card.cardId) else true
|
||||
|
||||
fun whereToNavigate(scanResponse: ScanResponse): AppScreen {
|
||||
return when (scanResponse.productType) {
|
||||
ProductType.Note -> AppScreen.OnboardingNote
|
||||
ProductType.Wallet -> AppScreen.OnboardingWallet
|
||||
ProductType.Twin -> AppScreen.OnboardingTwins
|
||||
ProductType.Other -> AppScreen.OnboardingOther
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
package com.tangem.tap.features.onboarding
|
||||
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.operations.attestation.CardVerifyAndGetInfo
|
||||
import com.tangem.operations.attestation.OnlineCardVerifier
|
||||
import com.tangem.tap.common.extensions.isPositive
|
||||
import com.tangem.tap.common.extensions.safeUpdate
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.UrlBitmapLoader
|
||||
import com.tangem.tap.domain.extensions.getOrLoadCardArtworkUrl
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.tap.persistence.UsedCardsPrefStorage
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.math.BigDecimal
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class OnboardingManager(
|
||||
var scanResponse: ScanResponse,
|
||||
val fromScreen: AppScreen,
|
||||
val usedCardsPrefStorage: UsedCardsPrefStorage,
|
||||
) {
|
||||
|
||||
var cardInfo: Result<CardVerifyAndGetInfo.Response.Item>? = null
|
||||
private set
|
||||
|
||||
suspend fun loadArtwork(): Artwork {
|
||||
cardInfo = OnlineCardVerifier().getCardInfo(scanResponse.card.cardId, scanResponse.card.cardPublicKey)
|
||||
return loadArtwork(scanResponse.card.getOrLoadCardArtworkUrl(cardInfo))
|
||||
}
|
||||
|
||||
suspend fun loadArtwork(url: String): Artwork {
|
||||
return withContext(Dispatchers.Main) {
|
||||
suspendCoroutine { continuation ->
|
||||
UrlBitmapLoader().loadBitmap(url) {
|
||||
val result = when (it) {
|
||||
is Result.Success -> Artwork(url, it.data)
|
||||
is Result.Failure -> Artwork(url, null)
|
||||
}
|
||||
continuation.resume(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateBalance(walletManager: WalletManager): OnboardingWalletBalance {
|
||||
return when (val result = walletManager.safeUpdate()) {
|
||||
is Result.Success -> {
|
||||
val wallet = walletManager.wallet
|
||||
val valueOfAmount = wallet.amounts[AmountType.Coin]?.value
|
||||
if (valueOfAmount == null) {
|
||||
val customError = TapError.CustomError("Amount is NULL")
|
||||
OnboardingWalletBalance.criticalError(customError)
|
||||
} else {
|
||||
val currency = Currency.Blockchain(walletManager.wallet.blockchain)
|
||||
val balance = if (valueOfAmount.isZero()) {
|
||||
OnboardingWalletBalance.done(valueOfAmount, wallet.recentTransactions.isNotEmpty(), currency)
|
||||
} else {
|
||||
OnboardingWalletBalance.done(valueOfAmount, false, currency)
|
||||
}
|
||||
balance
|
||||
}
|
||||
}
|
||||
is Result.Failure -> {
|
||||
val error = (result.error as? TapError) ?: TapError.UnknownError
|
||||
when (error) {
|
||||
is TapError.WalletManagerUpdate.NoAccountError -> OnboardingWalletBalance.error(error)
|
||||
// NoInternetConnection, WalletManagerUpdate.InternalError
|
||||
else -> OnboardingWalletBalance.criticalError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun activationStarted(cardId: String) {
|
||||
usedCardsPrefStorage.activationStarted(cardId)
|
||||
}
|
||||
|
||||
fun activationFinished(cardId: String) {
|
||||
usedCardsPrefStorage.activationFinished(cardId)
|
||||
}
|
||||
}
|
||||
|
||||
data class OnboardingWalletBalance(
|
||||
val value: BigDecimal = BigDecimal.ZERO,
|
||||
val currency: Currency.Blockchain = Currency.Blockchain(Blockchain.Unknown),
|
||||
val hasIncomingTransaction: Boolean = false,
|
||||
val state: ProgressState,
|
||||
val error: TapError? = null,
|
||||
val criticalError: TapError? = null,
|
||||
) {
|
||||
|
||||
fun balanceIsToppedUp(): Boolean = value.isPositive() || hasIncomingTransaction
|
||||
|
||||
val amountToCreateAccount: String?
|
||||
get() = if (error is TapError.WalletManagerUpdate.NoAccountError) error.customMessage else null
|
||||
|
||||
companion object {
|
||||
fun error(error: TapError): OnboardingWalletBalance = OnboardingWalletBalance(
|
||||
state = ProgressState.Error,
|
||||
error = error
|
||||
)
|
||||
|
||||
fun criticalError(error: TapError): OnboardingWalletBalance = OnboardingWalletBalance(
|
||||
state = ProgressState.Error,
|
||||
criticalError = error
|
||||
)
|
||||
|
||||
fun loading(value: BigDecimal = BigDecimal.ZERO): OnboardingWalletBalance = OnboardingWalletBalance(
|
||||
value,
|
||||
state = ProgressState.Loading
|
||||
)
|
||||
|
||||
fun done(value: BigDecimal, hasTransactions: Boolean, currency: Currency.Blockchain): OnboardingWalletBalance =
|
||||
OnboardingWalletBalance(value, currency, hasTransactions, ProgressState.Done)
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ abstract class BaseOnboardingFragment<T> : BaseStoreFragment(R.layout.fragment_o
|
|||
protected var fromScreen: AppScreen? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
fromScreen = store.state.globalState.onboardingService?.fromScreen
|
||||
fromScreen = store.state.globalState.onboardingManager?.fromScreen
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,18 @@
|
|||
package com.tangem.tap.features.onboarding.products.note
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.animation.OvershootInterpolator
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.constraintlayout.widget.ConstraintSet
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.transition.TransitionManager
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tangem_sdk_new.extensions.fadeIn
|
||||
import com.tangem.tangem_sdk_new.extensions.fadeOut
|
||||
import com.tangem.tap.common.extensions.getDrawableCompat
|
||||
import com.tangem.tap.common.extensions.stripZeroPlainString
|
||||
import com.tangem.tap.common.postUi
|
||||
|
|
@ -28,6 +33,7 @@ import kotlinx.android.synthetic.main.view_onboarding_tv_balance.*
|
|||
class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
|
||||
|
||||
private lateinit var btnRefreshBalanceWidget: RefreshBalanceWidget
|
||||
private var currentStep: OnboardingNoteStep = OnboardingNoteStep.None
|
||||
|
||||
override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_note
|
||||
|
||||
|
|
@ -36,11 +42,12 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
|
|||
|
||||
btnRefreshBalanceWidget = RefreshBalanceWidget(onboarding_main_container)
|
||||
val resources = AndroidResources(
|
||||
OnboardingStringResources(
|
||||
R.string.copy_toast_msg
|
||||
)
|
||||
OnboardingStringResources(
|
||||
R.string.copy_toast_msg
|
||||
)
|
||||
)
|
||||
|
||||
store.dispatch(OnboardingNoteAction.LoadCardArtwork)
|
||||
store.dispatch(OnboardingNoteAction.SetResources(resources))
|
||||
store.dispatch(OnboardingNoteAction.DetermineStepOfScreen)
|
||||
}
|
||||
|
|
@ -57,7 +64,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
|
|||
override fun newState(state: OnboardingNoteState) {
|
||||
if (activity == null) return
|
||||
|
||||
state.artworkBitmap?.let { imv_front_card.setImageBitmap(it) }
|
||||
state.cardArtwork?.let { imv_front_card.swapToBitmapDrawable(it.artwork) }
|
||||
pb_state.max = state.steps.size - 1
|
||||
pb_state.progress = state.progress
|
||||
|
||||
|
|
@ -69,13 +76,14 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
|
|||
}
|
||||
setBalance(state)
|
||||
showConfetti(state.showConfetti)
|
||||
currentStep = state.currentStep
|
||||
}
|
||||
|
||||
private fun setBalance(state: OnboardingNoteState) {
|
||||
if (state.balanceCurrency.blockchain == Blockchain.Unknown) return
|
||||
if (state.walletBalance.currency.blockchain == Blockchain.Unknown) return
|
||||
|
||||
val balanceValue = state.balanceValue.stripZeroPlainString()
|
||||
val currency = state.balanceCurrency.currencySymbol
|
||||
val balanceValue = state.walletBalance.value.stripZeroPlainString()
|
||||
val currency = state.walletBalance.currency.currencySymbol
|
||||
tv_balance_value.text = "$balanceValue $currency"
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +112,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
|
|||
tv_body.setText(R.string.onboarding_create_wallet_body)
|
||||
|
||||
imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_circle))
|
||||
updateConstraints(R.layout.lp_onboarding_create_wallet)
|
||||
updateConstraints(state.currentStep, R.layout.lp_onboarding_create_wallet)
|
||||
}
|
||||
|
||||
private fun setupTopUpWalletState(state: OnboardingNoteState) {
|
||||
|
|
@ -121,7 +129,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
|
|||
tv_header.setText(R.string.onboarding_top_up_header)
|
||||
tv_body.setText(R.string.onboarding_top_up_body)
|
||||
|
||||
btnRefreshBalanceWidget.changeState(state.balanceState)
|
||||
btnRefreshBalanceWidget.changeState(state.walletBalance.state)
|
||||
if (btnRefreshBalanceWidget.isShowing != true) {
|
||||
postUi(300) {
|
||||
btnRefreshBalanceWidget.mainView.setOnClickListener {
|
||||
|
|
@ -130,7 +138,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
|
|||
}
|
||||
}
|
||||
imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8))
|
||||
updateConstraints(R.layout.lp_onboarding_topup_wallet)
|
||||
updateConstraints(state.currentStep, R.layout.lp_onboarding_topup_wallet)
|
||||
}
|
||||
|
||||
private fun setupDoneState(state: OnboardingNoteState) {
|
||||
|
|
@ -149,10 +157,12 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
|
|||
tv_body.setText(R.string.onboarding_done_body)
|
||||
|
||||
imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8))
|
||||
updateConstraints(R.layout.lp_onboarding_done_activation)
|
||||
updateConstraints(state.currentStep, R.layout.lp_onboarding_done_activation)
|
||||
}
|
||||
|
||||
private fun updateConstraints(@LayoutRes layoutId: Int) {
|
||||
private fun updateConstraints(currentStep: OnboardingNoteStep, @LayoutRes layoutId: Int) {
|
||||
if (this.currentStep == currentStep) return
|
||||
|
||||
val constraintSet = ConstraintSet()
|
||||
constraintSet.clone(requireContext(), layoutId)
|
||||
constraintSet.applyTo(onboarding_main_container)
|
||||
|
|
@ -160,4 +170,13 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
|
|||
transition.interpolator = OvershootInterpolator()
|
||||
TransitionManager.beginDelayedTransition(onboarding_main_container, transition)
|
||||
}
|
||||
}
|
||||
|
||||
fun ImageView.swapToBitmapDrawable(bitmap: Bitmap?, duration: Long = 200) {
|
||||
if (drawable is BitmapDrawable && (drawable as BitmapDrawable).bitmap == bitmap) return
|
||||
|
||||
fadeOut(duration) {
|
||||
setImageBitmap(bitmap)
|
||||
fadeIn(duration)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.tap.features.onboarding.products.note.redux
|
||||
|
||||
import com.tangem.tap.features.onboarding.service.OnboardingWalletBalance
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
import org.rekotlin.Action
|
||||
|
||||
/**
|
||||
|
|
@ -9,11 +11,14 @@ import org.rekotlin.Action
|
|||
sealed class OnboardingNoteAction : Action {
|
||||
// from user, ui
|
||||
data class SetResources(val resources: AndroidResources) : OnboardingNoteAction()
|
||||
object LoadCardArtwork : OnboardingNoteAction()
|
||||
object CreateWallet : OnboardingNoteAction()
|
||||
object TopUp : OnboardingNoteAction()
|
||||
object ShowAddressInfoDialog : OnboardingNoteAction()
|
||||
|
||||
// from redux
|
||||
class SetArtworkUrl(val artwork: Artwork) : OnboardingNoteAction()
|
||||
data class SetWalletManager(val walletManager: WalletManager) : OnboardingNoteAction()
|
||||
object DetermineStepOfScreen : OnboardingNoteAction()
|
||||
object Done : OnboardingNoteAction()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.features.onboarding.products.note.redux
|
||||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.postUi
|
||||
import com.tangem.tap.common.redux.AppDialog
|
||||
|
|
@ -8,13 +9,16 @@ import com.tangem.tap.common.redux.AppState
|
|||
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.features.onboarding.service.balanceIsToppedUp
|
||||
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.wallet.redux.ProgressState
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.DispatchFunction
|
||||
import org.rekotlin.Middleware
|
||||
|
||||
class OnboardingNoteMiddleware {
|
||||
|
|
@ -26,44 +30,57 @@ class OnboardingNoteMiddleware {
|
|||
private val onboardingNoteMiddleware: Middleware<AppState> = { dispatch, state ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
handleNoteAction(action)
|
||||
handleNoteAction(action, dispatch)
|
||||
next(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleNoteAction(action: Action) {
|
||||
private fun handleNoteAction(action: Action, dispatch: DispatchFunction) {
|
||||
if (action !is OnboardingNoteAction) return
|
||||
val service = store.state.onboardingNoteState.onboardingService ?: return
|
||||
val globalState = store.state.globalState
|
||||
val onboardingManager = globalState.onboardingManager ?: return
|
||||
|
||||
val scanResponse = onboardingManager.scanResponse
|
||||
val card = onboardingManager.scanResponse.card
|
||||
val noteState = store.state.onboardingNoteState
|
||||
|
||||
when (action) {
|
||||
is OnboardingNoteAction.LoadCardArtwork -> {
|
||||
scope.launch {
|
||||
val artwork = onboardingManager.loadArtwork()
|
||||
withMainContext { store.dispatch(OnboardingNoteAction.SetArtworkUrl(artwork)) }
|
||||
}
|
||||
}
|
||||
is OnboardingNoteAction.DetermineStepOfScreen -> {
|
||||
val step = when {
|
||||
service.walletManager == null -> OnboardingNoteStep.CreateWallet
|
||||
service.balanceIsToppedUp() -> OnboardingNoteStep.Done
|
||||
!card.hasWallets() -> OnboardingNoteStep.CreateWallet
|
||||
noteState.walletBalance.balanceIsToppedUp() -> OnboardingNoteStep.Done
|
||||
else -> OnboardingNoteStep.TopUpWallet
|
||||
}
|
||||
store.dispatch((OnboardingNoteAction.SetStepOfScreen(step)))
|
||||
}
|
||||
is OnboardingNoteAction.SetStepOfScreen -> {
|
||||
if (action.step == OnboardingNoteStep.Done) {
|
||||
service.activationFinished()
|
||||
postUi(500) { store.dispatch(OnboardingNoteAction.Confetti.Show) }
|
||||
when (action.step) {
|
||||
OnboardingNoteStep.TopUpWallet -> {
|
||||
store.dispatch(OnboardingNoteAction.Balance.Update)
|
||||
}
|
||||
OnboardingNoteStep.Done -> {
|
||||
onboardingManager.activationFinished(card.cardId)
|
||||
postUi(500) { store.dispatch(OnboardingNoteAction.Confetti.Show) }
|
||||
}
|
||||
}
|
||||
}
|
||||
is OnboardingNoteAction.CreateWallet -> {
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.createProductWallet(service.scanResponse)
|
||||
val result = tangemSdkManager.createProductWallet(scanResponse)
|
||||
withMainContext {
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
val updatedResponse = service.scanResponse.copy(card = result.data)
|
||||
service.scanResponse = updatedResponse
|
||||
service.activationStarted()
|
||||
|
||||
val updatedResponse = scanResponse.copy(card = result.data)
|
||||
onboardingManager.scanResponse = updatedResponse
|
||||
onboardingManager.activationStarted(updatedResponse.card.cardId)
|
||||
store.dispatch(OnboardingNoteAction.SetStepOfScreen(OnboardingNoteStep.TopUpWallet))
|
||||
store.dispatch(OnboardingNoteAction.Balance.Update)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
// do nothing
|
||||
|
|
@ -73,44 +90,57 @@ private fun handleNoteAction(action: Action) {
|
|||
}
|
||||
}
|
||||
is OnboardingNoteAction.Balance.Update -> {
|
||||
val loadingBalance = service.getBalance().copy(
|
||||
state = ProgressState.Loading,
|
||||
error = null,
|
||||
criticalError = null
|
||||
val walletManager = if (noteState.walletManager != null) {
|
||||
noteState.walletManager
|
||||
} else {
|
||||
val wmFactory = globalState.tapWalletManager.walletManagerFactory
|
||||
val walletManager = wmFactory.makePrimaryWalletManager(scanResponse).guard {
|
||||
val message = "Loading cancelled. Cause: wallet manager didn't created"
|
||||
val customError = TapError.CustomError(message)
|
||||
store.dispatchErrorNotification(customError)
|
||||
return
|
||||
}
|
||||
dispatch(OnboardingNoteAction.SetWalletManager(walletManager))
|
||||
walletManager
|
||||
}
|
||||
|
||||
val balanceIsLoading = noteState.walletBalance.copy(
|
||||
state = ProgressState.Loading,
|
||||
error = null,
|
||||
criticalError = null
|
||||
)
|
||||
store.dispatch(OnboardingNoteAction.Balance.Set(loadingBalance))
|
||||
store.dispatch(OnboardingNoteAction.Balance.Set(balanceIsLoading))
|
||||
|
||||
scope.launch {
|
||||
val loadedBalance = service.updateBalance()
|
||||
if (loadedBalance.criticalError != null) {
|
||||
store.dispatchErrorNotification(loadedBalance.criticalError)
|
||||
}
|
||||
val loadedBalance = onboardingManager.updateBalance(walletManager)
|
||||
loadedBalance.criticalError?.let { store.dispatchErrorNotification(it) }
|
||||
withMainContext { store.dispatch(OnboardingNoteAction.Balance.Set(loadedBalance)) }
|
||||
}
|
||||
|
||||
}
|
||||
is OnboardingNoteAction.Balance.Set -> {
|
||||
if (service.balanceIsToppedUp()) {
|
||||
if (action.balance.balanceIsToppedUp()) {
|
||||
store.dispatch(OnboardingNoteAction.SetStepOfScreen(OnboardingNoteStep.Done))
|
||||
}
|
||||
}
|
||||
is OnboardingNoteAction.ShowAddressInfoDialog -> {
|
||||
val addressData = noteState.onboardingService?.getAddressData() ?: return
|
||||
val addressData = noteState.walletManager?.getAddressData() ?: return
|
||||
val addressWasCopied = noteState.resources.strings.addressWasCopied
|
||||
val appDialog = AppDialog.AddressInfoDialog(
|
||||
addressData,
|
||||
onCopyAddress = { store.dispatchToastNotification(addressWasCopied) },
|
||||
onExploreAddress = { store.dispatchOpenUrl(addressData.exploreUrl) }
|
||||
addressData,
|
||||
onCopyAddress = { store.dispatchToastNotification(addressWasCopied) },
|
||||
onExploreAddress = { store.dispatchOpenUrl(addressData.exploreUrl) }
|
||||
)
|
||||
store.dispatchDialogShow(appDialog)
|
||||
}
|
||||
is OnboardingNoteAction.TopUp -> {
|
||||
val topUpUrl = noteState.onboardingService?.getToUpUrl() ?: return
|
||||
val topUpUrl = noteState.walletManager?.getToUpUrl() ?: return
|
||||
store.dispatchOpenUrl(topUpUrl)
|
||||
}
|
||||
OnboardingNoteAction.Done -> {
|
||||
store.dispatch(GlobalAction.Onboarding.Deactivate)
|
||||
store.dispatch(GlobalAction.Onboarding.Stop)
|
||||
scope.launch {
|
||||
store.state.globalState.tapWalletManager.onCardScanned(service.scanResponse)
|
||||
globalState.tapWalletManager.onCardScanned(scanResponse)
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.tap.features.onboarding.products.note.redux
|
|||
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.features.onboarding.service.OnboardingNoteService
|
||||
import org.rekotlin.Action
|
||||
|
||||
class OnboardingNoteReducer {
|
||||
|
|
@ -15,34 +14,20 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingNoteSt
|
|||
var state = appState.onboardingNoteState
|
||||
|
||||
when (action) {
|
||||
is GlobalAction.Onboarding.Activate -> {
|
||||
if (action.onboardingService is OnboardingNoteService) {
|
||||
val service = action.onboardingService
|
||||
val balance = service.getBalance()
|
||||
state = state.copy(
|
||||
onboardingService = service,
|
||||
artworkBitmap = service.getArtwork().value?.artwork,
|
||||
balanceValue = balance.value,
|
||||
balanceCurrency = balance.currency,
|
||||
balanceState = balance.state,
|
||||
amountToCreateAccount = balance.amountToCreateAccount,
|
||||
showConfetti = false
|
||||
)
|
||||
}
|
||||
}
|
||||
GlobalAction.Onboarding.Deactivate -> {
|
||||
is GlobalAction.Onboarding.Start -> {
|
||||
state = OnboardingNoteState()
|
||||
}
|
||||
is OnboardingNoteAction.SetArtworkUrl -> {
|
||||
state = state.copy(cardArtwork = action.artwork)
|
||||
}
|
||||
is OnboardingNoteAction.SetWalletManager -> {
|
||||
state = state.copy(walletManager = action.walletManager)
|
||||
}
|
||||
is OnboardingNoteAction.SetResources -> {
|
||||
state = state.copy(resources = action.resources)
|
||||
}
|
||||
is OnboardingNoteAction.Balance.Set -> {
|
||||
state = state.copy(
|
||||
balanceValue = action.balance.value,
|
||||
balanceCurrency = action.balance.currency,
|
||||
balanceState = action.balance.state,
|
||||
amountToCreateAccount = action.balance.amountToCreateAccount,
|
||||
)
|
||||
state = state.copy(walletBalance = action.balance)
|
||||
}
|
||||
is OnboardingNoteAction.SetStepOfScreen -> {
|
||||
if (action.step != state.currentStep && state.steps.contains(action.step)) {
|
||||
|
|
|
|||
|
|
@ -1,28 +1,22 @@
|
|||
package com.tangem.tap.features.onboarding.products.note.redux
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tap.features.onboarding.service.OnboardingNoteService
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
import org.rekotlin.StateType
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class OnboardingNoteState(
|
||||
val onboardingService: OnboardingNoteService? = null,
|
||||
val walletManager: WalletManager? = null,
|
||||
val resources: AndroidResources = AndroidResources(),
|
||||
// UI
|
||||
val showConfetti: Boolean = false,
|
||||
val artworkBitmap: Bitmap? = null,
|
||||
val balanceValue: BigDecimal = BigDecimal.ZERO,
|
||||
val balanceCurrency: Currency = Currency.Blockchain(Blockchain.Unknown),
|
||||
val balanceState: ProgressState = ProgressState.Done,
|
||||
val amountToCreateAccount: String? = null,
|
||||
// UI
|
||||
val cardArtwork: Artwork? = null,
|
||||
val walletBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading(),
|
||||
val currentStep: OnboardingNoteStep = OnboardingNoteStep.None,
|
||||
val steps: List<OnboardingNoteStep> = OnboardingNoteStep.values().toList(),
|
||||
val showConfetti: Boolean = false,
|
||||
) : StateType {
|
||||
|
||||
val progress: Int
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import androidx.transition.TransitionManager
|
|||
import com.tangem.tap.common.extensions.getDrawableCompat
|
||||
import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
|
||||
import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
|
||||
import com.tangem.tap.features.onboarding.products.note.swapToBitmapDrawable
|
||||
import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsAction
|
||||
import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsState
|
||||
import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsStep
|
||||
|
|
@ -29,6 +30,7 @@ class OnboardingOtherCardsFragment : BaseOnboardingFragment<OnboardingOtherCards
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
store.dispatch(OnboardingOtherCardsAction.LoadCardArtwork)
|
||||
store.dispatch(OnboardingOtherCardsAction.DetermineStepOfScreen)
|
||||
}
|
||||
|
||||
|
|
@ -45,7 +47,7 @@ class OnboardingOtherCardsFragment : BaseOnboardingFragment<OnboardingOtherCards
|
|||
if (activity == null) return
|
||||
if (state.currentStep == OnboardingOtherCardsStep.None) return
|
||||
|
||||
state.artworkBitmap?.let { imv_front_card.setImageBitmap(it) }
|
||||
state.cardArtwork?.let { imv_front_card.swapToBitmapDrawable(it.artwork) }
|
||||
pb_state.max = state.steps.size - 1
|
||||
pb_state.progress = state.progress
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.features.onboarding.products.otherCards.redux
|
||||
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
import org.rekotlin.Action
|
||||
|
||||
/**
|
||||
|
|
@ -7,9 +8,11 @@ import org.rekotlin.Action
|
|||
*/
|
||||
sealed class OnboardingOtherCardsAction : Action {
|
||||
// from user, ui
|
||||
object LoadCardArtwork : OnboardingOtherCardsAction()
|
||||
object CreateWallet : OnboardingOtherCardsAction()
|
||||
|
||||
// from redux
|
||||
class SetArtworkUrl(val artwork: Artwork) : OnboardingOtherCardsAction()
|
||||
object DetermineStepOfScreen : OnboardingOtherCardsAction()
|
||||
object Done : OnboardingOtherCardsAction()
|
||||
|
||||
|
|
|
|||
|
|
@ -8,12 +8,14 @@ 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.domain.DELAY_SDK_DIALOG_CLOSE
|
||||
import com.tangem.tap.domain.extensions.hasWallets
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.DispatchFunction
|
||||
import org.rekotlin.Middleware
|
||||
|
||||
class OnboardingOtherCardsMiddleware {
|
||||
|
|
@ -25,40 +27,48 @@ class OnboardingOtherCardsMiddleware {
|
|||
private val onboardingOtherCardsMiddleware: Middleware<AppState> = { dispatch, state ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
handleOtherCardsAction(action)
|
||||
handleOtherCardsAction(action, dispatch)
|
||||
next(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleOtherCardsAction(action: Action) {
|
||||
private fun handleOtherCardsAction(action: Action, dispatch: DispatchFunction) {
|
||||
if (action !is OnboardingOtherCardsAction) return
|
||||
val service = store.state.onboardingOtherCardsState.onboardingService ?: return
|
||||
val noteState = store.state.onboardingOtherCardsState
|
||||
val globalState = store.state.globalState
|
||||
val onboardingManager = globalState.onboardingManager ?: return
|
||||
|
||||
val card = onboardingManager.scanResponse.card
|
||||
|
||||
when (action) {
|
||||
is OnboardingOtherCardsAction.LoadCardArtwork -> {
|
||||
scope.launch {
|
||||
val artwork = onboardingManager.loadArtwork()
|
||||
withMainContext { store.dispatch(OnboardingOtherCardsAction.SetArtworkUrl(artwork)) }
|
||||
}
|
||||
}
|
||||
is OnboardingOtherCardsAction.DetermineStepOfScreen -> {
|
||||
val step = when {
|
||||
service.walletManager == null -> OnboardingOtherCardsStep.CreateWallet
|
||||
!card.hasWallets() -> OnboardingOtherCardsStep.CreateWallet
|
||||
else -> OnboardingOtherCardsStep.Done
|
||||
}
|
||||
store.dispatch((OnboardingOtherCardsAction.SetStepOfScreen(step)))
|
||||
}
|
||||
is OnboardingOtherCardsAction.SetStepOfScreen -> {
|
||||
if (action.step == OnboardingOtherCardsStep.Done) {
|
||||
service.activationFinished()
|
||||
onboardingManager.activationFinished(card.cardId)
|
||||
postUi(500) { store.dispatch(OnboardingOtherCardsAction.Confetti.Show) }
|
||||
}
|
||||
}
|
||||
is OnboardingOtherCardsAction.CreateWallet -> {
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.createProductWallet(service.scanResponse)
|
||||
val result = tangemSdkManager.createProductWallet(onboardingManager.scanResponse)
|
||||
withMainContext {
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
val updatedResponse = service.scanResponse.copy(card = result.data)
|
||||
service.scanResponse = updatedResponse
|
||||
service.activationStarted()
|
||||
val updatedResponse = onboardingManager.scanResponse.copy(card = result.data)
|
||||
onboardingManager.scanResponse = updatedResponse
|
||||
onboardingManager.activationStarted(updatedResponse.card.cardId)
|
||||
|
||||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
store.dispatch(OnboardingOtherCardsAction.SetStepOfScreen(OnboardingOtherCardsStep.Done))
|
||||
|
|
@ -71,9 +81,9 @@ private fun handleOtherCardsAction(action: Action) {
|
|||
}
|
||||
}
|
||||
OnboardingOtherCardsAction.Done -> {
|
||||
store.dispatch(GlobalAction.Onboarding.Deactivate)
|
||||
store.dispatch(GlobalAction.Onboarding.Stop)
|
||||
scope.launch {
|
||||
store.state.globalState.tapWalletManager.onCardScanned(service.scanResponse)
|
||||
globalState.tapWalletManager.onCardScanned(onboardingManager.scanResponse)
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.tap.features.onboarding.products.otherCards.redux
|
|||
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.features.onboarding.service.OnboardingOtherCardsService
|
||||
import org.rekotlin.Action
|
||||
|
||||
class OnboardingOtherCardsReducer {
|
||||
|
|
@ -15,19 +14,12 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingOtherC
|
|||
var state = appState.onboardingOtherCardsState
|
||||
|
||||
when (action) {
|
||||
is GlobalAction.Onboarding.Activate -> {
|
||||
if (action.onboardingService is OnboardingOtherCardsService) {
|
||||
val service = action.onboardingService
|
||||
state = state.copy(
|
||||
onboardingService = service,
|
||||
artworkBitmap = service.getArtwork().value?.artwork,
|
||||
showConfetti = false
|
||||
)
|
||||
}
|
||||
}
|
||||
GlobalAction.Onboarding.Deactivate -> {
|
||||
is GlobalAction.Onboarding.Start -> {
|
||||
state = OnboardingOtherCardsState()
|
||||
}
|
||||
is OnboardingOtherCardsAction.SetArtworkUrl -> {
|
||||
state = state.copy(cardArtwork = action.artwork)
|
||||
}
|
||||
is OnboardingOtherCardsAction.SetStepOfScreen -> {
|
||||
if (action.step != state.currentStep && state.steps.contains(action.step)) {
|
||||
state = state.copy(currentStep = action.step)
|
||||
|
|
|
|||
|
|
@ -1,17 +1,15 @@
|
|||
package com.tangem.tap.features.onboarding.products.otherCards.redux
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import com.tangem.tap.features.onboarding.service.OnboardingOtherCardsService
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
import org.rekotlin.StateType
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class OnboardingOtherCardsState(
|
||||
val onboardingService: OnboardingOtherCardsService? = null,
|
||||
// UI
|
||||
// UI
|
||||
val cardArtwork: Artwork? = null,
|
||||
val showConfetti: Boolean = false,
|
||||
val artworkBitmap: Bitmap? = null,
|
||||
val currentStep: OnboardingOtherCardsStep = OnboardingOtherCardsStep.None,
|
||||
val steps: List<OnboardingOtherCardsStep> = OnboardingOtherCardsStep.values().toList(),
|
||||
) : StateType {
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
package com.tangem.tap.features.onboarding.service
|
||||
|
||||
import com.tangem.blockchain.common.WalletManagerFactory
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.domain.ProductType
|
||||
import com.tangem.tap.domain.extensions.hasWallets
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.preferencesStorage
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class OnboardingHelper {
|
||||
companion object {
|
||||
|
||||
fun isOnboardingCase(response: ScanResponse): Boolean = if (response.card.hasWallets())
|
||||
preferencesStorage.usedCardsPrefStorage.activationIsStarted(response.card.cardId) else true
|
||||
|
||||
fun createService(
|
||||
fromScreen: AppScreen,
|
||||
scanResponse: ScanResponse,
|
||||
walletManagerFactory: WalletManagerFactory,
|
||||
): ProductOnboardingService? {
|
||||
val cardInfoStorage = preferencesStorage.usedCardsPrefStorage
|
||||
return when(scanResponse.productType) {
|
||||
ProductType.Note -> OnboardingNoteService(fromScreen, scanResponse, cardInfoStorage, walletManagerFactory)
|
||||
ProductType.Wallet -> null
|
||||
ProductType.Twin -> null
|
||||
ProductType.Other -> OnboardingOtherCardsService(fromScreen, scanResponse, cardInfoStorage, walletManagerFactory)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
package com.tangem.tap.features.onboarding.service
|
||||
|
||||
import com.tangem.blockchain.common.WalletManagerFactory
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.persistence.UsedCardsPrefStorage
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class OnboardingNoteService(
|
||||
override val fromScreen: AppScreen,
|
||||
override var scanResponse: ScanResponse,
|
||||
cardInfoStorage: UsedCardsPrefStorage,
|
||||
walletManagerFactory: WalletManagerFactory,
|
||||
) : ProductOnboardingService(cardInfoStorage, walletManagerFactory) {
|
||||
|
||||
override fun navigateToScreen(): AppScreen {
|
||||
return AppScreen.OnboardingNote
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
package com.tangem.tap.features.onboarding.service
|
||||
|
||||
import com.tangem.blockchain.common.WalletManagerFactory
|
||||
import com.tangem.tap.common.extensions.withMainContext
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.features.wallet.redux.AddressData
|
||||
import com.tangem.tap.persistence.UsedCardsPrefStorage
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class OnboardingOtherCardsService(
|
||||
override val fromScreen: AppScreen,
|
||||
override var scanResponse: ScanResponse,
|
||||
cardInfoStorage: UsedCardsPrefStorage,
|
||||
walletManagerFactory: WalletManagerFactory,
|
||||
) : ProductOnboardingService(cardInfoStorage, walletManagerFactory) {
|
||||
|
||||
override fun navigateToScreen(): AppScreen {
|
||||
return AppScreen.OnboardingOther
|
||||
}
|
||||
|
||||
// Return any value and any state. The balance of the card is not important in this service
|
||||
override suspend fun updateBalance(): OnboardingWalletBalance {
|
||||
val customError = TapError.CustomError("Loading cancelled. Cause: wallet manager didn't created")
|
||||
withMainContext { loadedBalance = OnboardingWalletBalance.error(customError) }
|
||||
return loadedBalance
|
||||
}
|
||||
|
||||
override fun getToUpUrl(): String? {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun getAddressData(): AddressData? {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,291 +0,0 @@
|
|||
package com.tangem.tap.features.onboarding.service
|
||||
|
||||
import android.util.Log
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.operations.attestation.OnlineCardVerifier
|
||||
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
|
||||
import com.tangem.tap.common.extensions.isPositive
|
||||
import com.tangem.tap.common.extensions.safeUpdate
|
||||
import com.tangem.tap.common.extensions.withMainContext
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.UrlBitmapLoader
|
||||
import com.tangem.tap.domain.extensions.getArtworkUrl
|
||||
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.domain.topup.TradeCryptoHelper
|
||||
import com.tangem.tap.features.wallet.redux.AddressData
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.tap.features.wallet.redux.reducers.createAddressesData
|
||||
import com.tangem.tap.persistence.UsedCardsPrefStorage
|
||||
import com.tangem.tap.store
|
||||
import kotlinx.coroutines.*
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import java.math.BigDecimal
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface OnboardingService {
|
||||
val fromScreen: AppScreen
|
||||
val scanResponse: ScanResponse
|
||||
|
||||
// val cardInfoStorage: UsedCardsPrefStorage
|
||||
var walletManager: WalletManager?
|
||||
|
||||
var onInitializationProgress: ((ProgressState) -> Unit)?
|
||||
var onInitialized: ((AppScreen) -> Unit)?
|
||||
var onError: ((TapError) -> Unit)?
|
||||
|
||||
fun initializeOnboarding()
|
||||
fun getArtwork(): OnboardingArtwork
|
||||
fun getBalance(): OnboardingWalletBalance
|
||||
suspend fun updateBalance(): OnboardingWalletBalance
|
||||
fun getToUpUrl(): String?
|
||||
fun getAddressData(): AddressData?
|
||||
|
||||
fun activationStarted()
|
||||
fun activationFinished()
|
||||
}
|
||||
|
||||
abstract class ProductOnboardingService(
|
||||
private val cardInfoStorage: UsedCardsPrefStorage,
|
||||
private val walletManagerFactory: WalletManagerFactory,
|
||||
) : OnboardingService {
|
||||
|
||||
override var walletManager: WalletManager? = null
|
||||
|
||||
override var onInitializationProgress: ((ProgressState) -> Unit)? = null
|
||||
override var onInitialized: ((AppScreen) -> Unit)? = null
|
||||
override var onError: ((TapError) -> Unit)? = null
|
||||
|
||||
protected var isProceeded = false
|
||||
|
||||
protected var loadedCardArtwork: OnboardingArtwork = OnboardingArtwork.loading()
|
||||
set(value) {
|
||||
field = value
|
||||
tryToProceed()
|
||||
}
|
||||
|
||||
protected var loadedBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading()
|
||||
set(value) {
|
||||
field = value
|
||||
tryToProceed()
|
||||
}
|
||||
|
||||
protected val internalScope = CoroutineScope(Job() + Dispatchers.IO + coroutineExceptionHandler())
|
||||
|
||||
override fun initializeOnboarding() {
|
||||
val card = scanResponse.card
|
||||
onInitializationProgress?.invoke(ProgressState.Loading)
|
||||
|
||||
internalScope.launch {
|
||||
val artwork = loadCardArtwork(card)
|
||||
withMainContext {
|
||||
loadedCardArtwork = if (artwork == null) OnboardingArtwork.error() else OnboardingArtwork.done(artwork)
|
||||
}
|
||||
}
|
||||
internalScope.launch { updateBalance() }
|
||||
}
|
||||
|
||||
override fun getArtwork(): OnboardingArtwork = loadedCardArtwork
|
||||
|
||||
override fun getBalance(): OnboardingWalletBalance = loadedBalance
|
||||
|
||||
override fun getToUpUrl(): String? {
|
||||
val wallet = walletManager?.wallet ?: return null
|
||||
val config = store.state.globalState.configManager?.config ?: return null
|
||||
val defaultAddress = wallet.address
|
||||
|
||||
return TradeCryptoHelper.getUrl(
|
||||
TradeCryptoHelper.Action.Buy,
|
||||
wallet.blockchain,
|
||||
wallet.blockchain.currency,
|
||||
defaultAddress,
|
||||
config.moonPayApiKey,
|
||||
config.moonPayApiSecretKey
|
||||
)
|
||||
}
|
||||
|
||||
override fun getAddressData(): AddressData? {
|
||||
val wallet = walletManager?.wallet ?: return null
|
||||
|
||||
val addressDataList = wallet.createAddressesData()
|
||||
return if (addressDataList.isEmpty()) null
|
||||
else addressDataList[0]
|
||||
}
|
||||
|
||||
protected suspend fun loadCardArtwork(card: Card): Artwork? {
|
||||
return when (val cardInfoResult = OnlineCardVerifier().getCardInfo(card.cardId, card.cardPublicKey)) {
|
||||
is Result.Success -> {
|
||||
val artworkUrl = card.getArtworkUrl(cardInfoResult.data.artwork?.id) ?: Artwork.DEFAULT_IMG_URL
|
||||
loadBitmap(artworkUrl)
|
||||
}
|
||||
is Result.Failure -> {
|
||||
loadBitmap(Artwork.DEFAULT_IMG_URL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun updateBalance(): OnboardingWalletBalance {
|
||||
withMainContext { loadedBalance = OnboardingWalletBalance.loading(loadedBalance.value) }
|
||||
|
||||
val walletManager = walletManager ?: walletManagerFactory.makePrimaryWalletManager(scanResponse).guard {
|
||||
val customError = TapError.CustomError("Loading cancelled. Cause: wallet manager didn't created")
|
||||
withMainContext { loadedBalance = OnboardingWalletBalance.error(customError) }
|
||||
return loadedBalance
|
||||
}
|
||||
|
||||
this.walletManager = walletManager
|
||||
val currency = Currency.Blockchain(walletManager.wallet.blockchain)
|
||||
val updatedBalance = when (val result = walletManager.safeUpdate()) {
|
||||
is Result.Success -> {
|
||||
val wallet = walletManager.wallet
|
||||
val valueOfAmount = wallet.amounts[AmountType.Coin]?.value
|
||||
if (valueOfAmount == null) {
|
||||
val customError = TapError.CustomError("Amount is NULL")
|
||||
OnboardingWalletBalance.criticalError(customError)
|
||||
} else {
|
||||
val balance = if (valueOfAmount.isZero()) {
|
||||
OnboardingWalletBalance.done(valueOfAmount, hasForIncomingTransactions(wallet), currency)
|
||||
} else {
|
||||
OnboardingWalletBalance.done(valueOfAmount, false, currency)
|
||||
}
|
||||
balance
|
||||
}
|
||||
}
|
||||
is Result.Failure -> {
|
||||
val error = (result.error as? TapError) ?: TapError.UnknownError
|
||||
when (error) {
|
||||
is TapError.WalletManagerUpdate.NoAccountError -> OnboardingWalletBalance.error(error)
|
||||
// NoInternetConnection, WalletManagerUpdate.InternalError
|
||||
else -> OnboardingWalletBalance.criticalError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
withMainContext { loadedBalance = updatedBalance }
|
||||
return loadedBalance
|
||||
}
|
||||
|
||||
override fun activationStarted() {
|
||||
cardInfoStorage.activationStarted(scanResponse.card.cardId)
|
||||
}
|
||||
|
||||
override fun activationFinished() {
|
||||
cardInfoStorage.activationFinished(scanResponse.card.cardId)
|
||||
}
|
||||
|
||||
protected open suspend fun hasForIncomingTransactions(wallet: Wallet): Boolean {
|
||||
return wallet.recentTransactions.isNotEmpty()
|
||||
}
|
||||
|
||||
protected open fun tryToProceed() {
|
||||
if (isProceeded) return
|
||||
|
||||
loadedBalance.criticalError?.let {
|
||||
sendError(it)
|
||||
return
|
||||
}
|
||||
if (!isReadyToProceed()) return
|
||||
|
||||
isProceeded = true
|
||||
onInitializationProgress?.invoke(ProgressState.Done)
|
||||
onInitialized?.invoke(navigateToScreen())
|
||||
}
|
||||
|
||||
protected open fun isReadyToProceed(): Boolean {
|
||||
return loadedBalance.state != ProgressState.Loading && loadedCardArtwork.state != ProgressState.Loading
|
||||
}
|
||||
|
||||
protected fun sendError(error: TapError) {
|
||||
onInitializationProgress?.invoke(ProgressState.Error)
|
||||
onError?.invoke(error)
|
||||
}
|
||||
|
||||
protected abstract fun navigateToScreen(): AppScreen
|
||||
|
||||
private suspend fun loadBitmap(url: String): Artwork? {
|
||||
return withContext(Dispatchers.Main) {
|
||||
suspendCoroutine { continuation ->
|
||||
UrlBitmapLoader().loadBitmap(url) {
|
||||
val result = when (it) {
|
||||
is Result.Success -> Artwork(url, it.data)
|
||||
is Result.Failure -> null
|
||||
}
|
||||
continuation.resume(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun coroutineExceptionHandler(): CoroutineExceptionHandler = CoroutineExceptionHandler { _, throwable ->
|
||||
StringWriter().apply {
|
||||
throwable.printStackTrace(PrintWriter(this))
|
||||
Log.e("Coroutine", this.toString())
|
||||
store.dispatchDebugErrorNotification(this.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class OnboardingArtwork(
|
||||
val value: Artwork? = null,
|
||||
val state: ProgressState
|
||||
) {
|
||||
companion object {
|
||||
// balance was not loaded at all when any error occurred
|
||||
fun error(): OnboardingArtwork = OnboardingArtwork(state = ProgressState.Error)
|
||||
|
||||
// balance is loading
|
||||
fun loading(): OnboardingArtwork = OnboardingArtwork(state = ProgressState.Loading)
|
||||
|
||||
// balance was loaded
|
||||
fun done(artwork: Artwork): OnboardingArtwork = OnboardingArtwork(artwork, ProgressState.Done)
|
||||
}
|
||||
}
|
||||
|
||||
data class OnboardingWalletBalance(
|
||||
val value: BigDecimal = BigDecimal.ZERO,
|
||||
val currency: Currency.Blockchain = Currency.Blockchain(Blockchain.Unknown),
|
||||
val hasIncomingTransaction: Boolean = false,
|
||||
val state: ProgressState,
|
||||
val error: TapError? = null,
|
||||
val criticalError: TapError? = null,
|
||||
) {
|
||||
|
||||
val amountToCreateAccount: String?
|
||||
get() = if (error is TapError.WalletManagerUpdate.NoAccountError) error.customMessage else null
|
||||
|
||||
companion object {
|
||||
fun error(error: TapError): OnboardingWalletBalance = OnboardingWalletBalance(
|
||||
state = ProgressState.Error,
|
||||
error = error
|
||||
)
|
||||
|
||||
fun criticalError(error: TapError): OnboardingWalletBalance = OnboardingWalletBalance(
|
||||
state = ProgressState.Error,
|
||||
criticalError = error
|
||||
)
|
||||
|
||||
fun loading(value: BigDecimal = BigDecimal.ZERO): OnboardingWalletBalance = OnboardingWalletBalance(
|
||||
value,
|
||||
state = ProgressState.Loading
|
||||
)
|
||||
|
||||
fun done(value: BigDecimal, hasTransactions: Boolean, currency: Currency.Blockchain): OnboardingWalletBalance =
|
||||
OnboardingWalletBalance(value, currency, hasTransactions, ProgressState.Done)
|
||||
}
|
||||
}
|
||||
|
||||
fun ProductOnboardingService.balanceIsToppedUp(): Boolean {
|
||||
val balance = getBalance()
|
||||
return balance.value.isPositive() || balance.hasIncomingTransaction
|
||||
}
|
||||
|
|
@ -151,7 +151,7 @@ sealed class WalletAction : Action {
|
|||
object ShowOnboarding : TwinsAction()
|
||||
object SetOnboardingShown : TwinsAction()
|
||||
data class SetTwinCard(
|
||||
val secondCardId: String, val number: TwinCardNumber,
|
||||
val secondCardId: String?, val number: TwinCardNumber,
|
||||
val isCreatingTwinCardsAllowed: Boolean,
|
||||
) : TwinsAction()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue