diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt index 38c76819b3..ed4dbe69aa 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt @@ -11,6 +11,7 @@ import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOt import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletReducer import com.tangem.tap.features.send.redux.reducers.SendScreenReducer import com.tangem.tap.features.tokens.redux.TokensReducer +import com.tangem.tap.features.twins.redux.TwinCardsReducer import com.tangem.tap.features.wallet.redux.reducers.WalletReducer import org.rekotlin.Action @@ -26,6 +27,7 @@ fun appReducer(action: Action, state: AppState?): AppState { onboardingWalletState = OnboardingWalletReducer.reduce(action, state), onboardingOtherCardsState = OnboardingOtherCardsReducer.reduce(action, state), walletState = WalletReducer.reduce(action, state), + twinCardsState = TwinCardsReducer.reduce(action, state), sendState = SendScreenReducer.reduce(action, state.sendState), detailsState = DetailsReducer.reduce(action, state), disclaimerState = DisclaimerReducer.reduce(action, state), diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index f883d78840..98827674a0 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -22,7 +22,8 @@ import com.tangem.tap.features.send.redux.middlewares.SendMiddleware import com.tangem.tap.features.send.redux.states.SendState import com.tangem.tap.features.tokens.redux.TokensMiddleware import com.tangem.tap.features.tokens.redux.TokensState -import com.tangem.tap.features.twins.redux.CreateTwinWalletMiddleware +import com.tangem.tap.features.twins.redux.TwinCardsMiddleware +import com.tangem.tap.features.twins.redux.TwinCardsState import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.features.wallet.redux.middlewares.WalletMiddleware import org.rekotlin.Middleware @@ -36,6 +37,7 @@ data class AppState( val onboardingWalletState: OnboardingWalletState = OnboardingWalletState(), val onboardingOtherCardsState: OnboardingOtherCardsState = OnboardingOtherCardsState(), val walletState: WalletState = WalletState(), + val twinCardsState: TwinCardsState = TwinCardsState(), val sendState: SendState = SendState(), val detailsState: DetailsState = DetailsState(), val disclaimerState: DisclaimerState = DisclaimerState(), @@ -53,7 +55,7 @@ data class AppState( OnboardingWalletMiddleware.handler, OnboardingOtherCardsMiddleware.handler, WalletMiddleware().walletMiddleware, - CreateTwinWalletMiddleware.handler, + TwinCardsMiddleware.handler, SendMiddleware().sendMiddleware, DetailsMiddleware().detailsMiddleware, DisclaimerMiddleware().disclaimerMiddleware, diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index 163359460d..edc4601d8c 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -19,6 +19,7 @@ import com.tangem.tap.domain.tokens.CardCurrencies import com.tangem.tap.domain.twins.getTwinCardNumber import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.tap.features.tokens.redux.TokensAction +import com.tangem.tap.features.twins.redux.TwinCardsAction import com.tangem.tap.features.wallet.redux.Currency import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.network.NetworkConnectivity @@ -82,7 +83,8 @@ class TapWalletManager { FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.CARD_IS_SCANNED, data.card, data.walletData?.blockchain) } store.state.globalState.feedbackManager?.infoHolder?.setCardInfo(data.card) - updateConfigManager(data) + val configManager = store.state.globalState.configManager + updateConfigManager(configManager, data) withContext(Dispatchers.Main) { store.dispatch(WalletAction.ResetState) @@ -91,7 +93,8 @@ class TapWalletManager { store.dispatch(WalletAction.MultiWallet.SetIsMultiwalletAllowed(data.card.isMultiwalletAllowed)) if (data.card.isTangemTwin()) { data.card.getTwinCardNumber()?.let { - store.dispatch(WalletAction.TwinsAction.SetTwinCard(null, it, true)) + val isCreatingTwinsAllowed = configManager?.config?.isCreatingTwinCardsAllowed ?: false + store.dispatch(TwinCardsAction.SetTwinCard(it, null, isCreatingTwinsAllowed)) } } @@ -104,8 +107,7 @@ class TapWalletManager { } } - private fun updateConfigManager(data: ScanResponse) { - val configManager = store.state.globalState.configManager + private fun updateConfigManager(configManager: ConfigManager?, data: ScanResponse) { val blockchain = data.getBlockchain() if (data.card.isStart2Coin) { configManager?.turnOff(ConfigManager.isSendingToPayIdEnabled) @@ -122,53 +124,42 @@ class TapWalletManager { suspend fun loadData(data: ScanResponse) { withContext(Dispatchers.Main) { - store.dispatch(WalletAction.LoadCardInfo(data.card)) + getActionIfUnknownBlockchainOrEmptyWallet(data)?.let { + store.dispatch(it) + return@withContext + } - when { - data.card.isTangemTwin() && data.secondTwinPublicKey == null -> { - store.dispatch(WalletAction.EmptyWallet) + val config = store.state.globalState.configManager?.config ?: return@withContext + val blockchain = data.getBlockchain() + val primaryWalletManager = walletManagerFactory.makePrimaryWalletManager(data) + + if (blockchain != Blockchain.Unknown && primaryWalletManager != null) { + val primaryToken = data.getPrimaryToken() + + store.dispatch(WalletAction.MultiWallet.SetPrimaryBlockchain(blockchain)) + if (primaryToken != null) { + primaryWalletManager.addToken(primaryToken) + store.dispatch(WalletAction.MultiWallet.SetPrimaryToken(primaryToken)) } - data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> { - store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain)) + if (data.card.isMultiwalletAllowed) { + loadMultiWalletData(data.card, blockchain, primaryWalletManager) + } else { + store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager)) + store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(blockchain))) } - data.card.wallets.isEmpty() -> { - store.dispatch(WalletAction.EmptyWallet) - } - else -> { - val config = store.state.globalState.configManager?.config ?: return@withContext - val blockchain = data.getBlockchain() - val primaryWalletManager = walletManagerFactory.makePrimaryWalletManager(data) - - if (blockchain != Blockchain.Unknown && primaryWalletManager != null) { - val primaryToken = data.getPrimaryToken() - - store.dispatch(WalletAction.MultiWallet.SetPrimaryBlockchain(blockchain)) - if (primaryToken != null) { - primaryWalletManager.addToken(primaryToken) - store.dispatch(WalletAction.MultiWallet.SetPrimaryToken(primaryToken)) - } - if (data.card.isMultiwalletAllowed) { - loadMultiWalletData(data.card, blockchain, primaryWalletManager) - } else { - store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager)) - store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(blockchain))) - } - - } else { - if (data.card.isMultiwalletAllowed) { - loadMultiWalletData(data.card, blockchain, null) - } - } - val moonPayUserStatus = store.state.globalState.moonPayUserStatus - store.dispatch(WalletAction.LoadWallet( - allowToBuy = config.isTopUpEnabled && moonPayUserStatus?.isBuyAllowed == true, - allowToSell = config.isTopUpEnabled && moonPayUserStatus?.isSellAllowed == true, - )) - store.dispatch(WalletAction.LoadFiatRate()) + } else { + if (data.card.isMultiwalletAllowed) { + loadMultiWalletData(data.card, blockchain, null) } } + val moonPayUserStatus = store.state.globalState.moonPayUserStatus + store.dispatch(WalletAction.LoadWallet( + allowToBuy = config.isTopUpEnabled && moonPayUserStatus?.isBuyAllowed == true, + allowToSell = config.isTopUpEnabled && moonPayUserStatus?.isSellAllowed == true, + )) + store.dispatch(WalletAction.LoadFiatRate()) } } @@ -220,28 +211,38 @@ class TapWalletManager { suspend fun reloadData(data: ScanResponse) { withContext(Dispatchers.Main) { - when { - data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> { - store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain)) - } - data.card.wallets.isEmpty() || - (data.card.isTangemTwin() && data.secondTwinPublicKey == null) -> { - store.dispatch(WalletAction.EmptyWallet) - } - else -> { - if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) { - store.dispatch(WalletAction.LoadData.Failure(TapError.NoInternetConnection)) - return@withContext - } - val config = store.state.globalState.configManager?.config ?: return@withContext - val moonpayUserStatus = store.state.globalState.moonPayUserStatus - store.dispatch(WalletAction.LoadWallet( - allowToBuy = config.isTopUpEnabled && moonpayUserStatus?.isBuyAllowed == true, - allowToSell = config.isTopUpEnabled && moonpayUserStatus?.isSellAllowed == true, - )) - store.dispatch(WalletAction.LoadFiatRate()) - } + getActionIfUnknownBlockchainOrEmptyWallet(data)?.let { + store.dispatch(it) + return@withContext } + if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) { + store.dispatch(WalletAction.LoadData.Failure(TapError.NoInternetConnection)) + return@withContext + } + + val config = store.state.globalState.configManager?.config ?: return@withContext + val moonpayUserStatus = store.state.globalState.moonPayUserStatus + store.dispatch(WalletAction.LoadWallet( + allowToBuy = config.isTopUpEnabled && moonpayUserStatus?.isBuyAllowed == true, + allowToSell = config.isTopUpEnabled && moonpayUserStatus?.isSellAllowed == true, + )) + store.dispatch(WalletAction.LoadFiatRate()) + } + } + + private fun getActionIfUnknownBlockchainOrEmptyWallet(data: ScanResponse): WalletAction? { + return when { + // check order is important + data.card.isTangemTwin() && !data.twinsIsTwinned() -> { + WalletAction.EmptyWallet + } + data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> { + WalletAction.LoadData.Failure(TapError.UnknownBlockchain) + } + data.card.wallets.isEmpty() -> { + WalletAction.EmptyWallet + } + else -> null } } diff --git a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt index 9003c9f4fc..01ef833c3d 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt @@ -28,10 +28,13 @@ object TapWorkarounds { return excludedBatch || excludedIssuerName } - fun Card.isTangemNote(): Boolean = tangemNoteBatches.contains(batchId) - fun Card.isTangemWallet(): Boolean = tangemWalletBatches.contains(batchId) + @Deprecated("Use ScanResponse.isTangemNote") + fun isTangemNote(card: Card): Boolean = tangemNoteBatches.contains(card.batchId) - fun Card.getTangemNoteBlockchain(): Blockchain? = tangemNoteBatches[batchId] + @Deprecated("Use ScanResponse.isTangemWallet") + fun isTangemWallet(card: Card): Boolean = tangemWalletBatches.contains(card.batchId) + + fun getTangemNoteBlockchain(card: Card): Blockchain? = tangemNoteBatches[card.batchId] private const val START_2_COIN_ISSUER = "start2coin" private const val TEST_CARD_BATCH = "99FF" @@ -64,7 +67,7 @@ val DELAY_SDK_DIALOG_CLOSE = 1400L val Card.isMultiwalletAllowed: Boolean get() { - return !isTangemTwin() && !isStart2Coin && !isTangemNote() + return !isTangemTwin() && !isStart2Coin && !isTangemNote(this) && (firmwareVersion >= FirmwareVersion.MultiWalletAvailable || getSingleWallet()?.curve == EllipticCurve.Secp256k1) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt index 3b01ae1c98..8b3c26a91e 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt @@ -40,7 +40,7 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { if (card.isTangemTwin()) { dealWithTwinCard(card, session, callback) - } else if (!card.isTangemNote() && card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable) { + } else if (!isTangemNote(card) && card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable) { createMissingWalletsIfNeeded(card, session, callback) } else { callback(CompletionResult.Success( diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt index 92beb01238..f1024cdf11 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletAndRescanTask.kt @@ -64,7 +64,7 @@ private class CreateWalletTangemNote : ProductCommandProcessor { override fun run( @@ -74,9 +82,9 @@ class ScanProductTask(val card: Card? = null) : CardSessionRunnable ScanNoteProcessor() - card.isTangemTwin() -> ScanTwinProcessor() - card.isTangemWallet() -> ScanWalletProcessor() + TapWorkarounds.isTangemNote(card) -> ScanNoteProcessor() + TwinsHelper.getTwinCardNumber(card.cardId) != null -> ScanTwinProcessor() + TapWorkarounds.isTangemWallet(card) -> ScanWalletProcessor() else -> ScanOtherCardsProcessor() } commandProcessor.proceed(card, session, callback) diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt index 89affa15d1..d74d5ccf0d 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt @@ -51,6 +51,7 @@ enum class TwinCardNumber(val number: Int) { } } +@Deprecated("Use ScanResponse.isTangemTwin") fun Card.isTangemTwin(): Boolean { return TwinsHelper.getTwinCardNumber(cardId) != null } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt index 0e7a1f5e21..3f8fbfccea 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt @@ -1,7 +1,6 @@ package com.tangem.tap.features.details.redux import com.tangem.blockchain.common.Wallet -import com.tangem.common.card.Card import com.tangem.operations.pins.CheckUserCodesResponse import com.tangem.tap.common.redux.NotificationAction import com.tangem.tap.common.redux.global.FiatCurrencyName @@ -14,10 +13,8 @@ import org.rekotlin.Action sealed class DetailsAction : Action { data class PrepareScreen( - val card: Card, val scanResponse: ScanResponse, val wallets: List, - val isCreatingTwinWalletAllowed: Boolean?, val cardTou: CardTou, val fiatCurrencyName: FiatCurrencyName, val fiatCurrencies: List? = null, diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index 5989a084e2..52c9652e68 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -7,9 +7,6 @@ import com.tangem.tap.domain.TapWorkarounds.isStart2Coin import com.tangem.tap.domain.extensions.isWalletDataSupported import com.tangem.tap.domain.extensions.signedHashesCount import com.tangem.tap.domain.extensions.toSendableAmounts -import com.tangem.tap.domain.twins.getTwinCardNumber -import com.tangem.tap.domain.twins.isTangemTwin -import com.tangem.tap.features.twins.redux.CreateTwinWalletState import com.tangem.tap.features.wallet.models.hasPendingTransactions import org.rekotlin.Action import java.util.* @@ -42,26 +39,12 @@ private fun internalReduce(action: Action, state: AppState): DetailsState { } private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: DetailsState): DetailsState { - val twinsState = if (action.card.isTangemTwin()) { - CreateTwinWalletState( - scanResponse = action.scanResponse, - twinCardNumber = action.card.getTwinCardNumber(), - createTwinWallet = null, - showAlert = false, - allowRecreatingWallet = action.isCreatingTwinWalletAllowed - ) - } else { - null - } - return DetailsState( - card = action.card, wallets = action.wallets, - cardInfo = action.card.toCardInfo(), - appCurrencyState = AppCurrencyState( - action.fiatCurrencyName - ), - createTwinWalletState = twinsState, - cardTermsOfUseUrl = action.cardTou.getUrl(action.card) + card = action.scanResponse.card, + wallets = action.wallets, + cardInfo = action.scanResponse.card.toCardInfo(), + appCurrencyState = AppCurrencyState(action.fiatCurrencyName), + cardTermsOfUseUrl = action.cardTou.getUrl(action.scanResponse.card) ) } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt index bd55c9aa95..40a6a6df31 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt @@ -6,10 +6,12 @@ import com.tangem.common.card.Card import com.tangem.tap.common.entities.Button import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY import com.tangem.tap.common.redux.global.FiatCurrencyName -import com.tangem.tap.features.twins.redux.CreateTwinWalletState +import com.tangem.tap.features.twins.redux.TwinCardsState import com.tangem.tap.network.coinmarketcap.FiatCurrency +import com.tangem.tap.store import org.rekotlin.StateType import java.util.* +import kotlin.properties.ReadOnlyProperty data class DetailsState( val card: Card? = null, @@ -19,9 +21,18 @@ data class DetailsState( val eraseWalletState: EraseWalletState? = null, val confirmScreenState: ConfirmScreenState? = null, val securityScreenState: SecurityScreenState? = null, - val createTwinWalletState: CreateTwinWalletState? = null, val cardTermsOfUseUrl: Uri? = null, -) : StateType +) : StateType { + + // if you do not delegate - the application crashes on startup, + // because twinCardsState has not been created yet + val twinCardsState: TwinCardsState by ReadOnlyProperty { thisRef, property -> + store.state.twinCardsState + } + + val isTangemTwins: Boolean + get() = store.state.globalState.scanResponse?.isTangemTwins() == true +} data class CardInfo( val cardId: String, diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt index 7d845228f7..81b6e91032 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt @@ -17,8 +17,8 @@ import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.features.details.redux.DetailsState import com.tangem.tap.features.details.redux.SecurityOption import com.tangem.tap.features.feedback.FeedbackEmail -import com.tangem.tap.features.twins.redux.CreateTwinWallet -import com.tangem.tap.features.twins.redux.CreateTwinWalletAction +import com.tangem.tap.features.twins.redux.CreateTwinWalletMode +import com.tangem.tap.features.twins.redux.TwinCardsAction import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_details.* @@ -91,12 +91,15 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber { - state.copy(createTwinWalletState = CreateTwinWalletState( - scanResponse = null, - twinCardNumber = action.twinCardNumber - ?: state.createTwinWalletState?.twinCardNumber, - createTwinWallet = action.createTwinWallet, - showAlert = false, - allowRecreatingWallet = state.createTwinWalletState?.allowRecreatingWallet - )) - } - CreateTwinWalletAction.NotEmpty -> state - CreateTwinWalletAction.ShowAlert -> { - state.copy(createTwinWalletState = state.createTwinWalletState?.copy( - showAlert = true - )) - } - CreateTwinWalletAction.HideAlert -> { - state.copy(createTwinWalletState = state.createTwinWalletState?.copy( - showAlert = false - )) - } - is CreateTwinWalletAction.Proceed -> { - state - } - CreateTwinWalletAction.Cancel -> state - CreateTwinWalletAction.Cancel.Confirm -> state - is CreateTwinWalletAction.LaunchFirstStep -> state - - CreateTwinWalletAction.LaunchFirstStep.Success -> { - state.copy(createTwinWalletState = state.createTwinWalletState?.copy( - step = CreateTwinWalletStep.SecondStep - )) - } - CreateTwinWalletAction.LaunchFirstStep.Failure -> state - - is CreateTwinWalletAction.LaunchSecondStep -> state - CreateTwinWalletAction.LaunchSecondStep.Success -> - state.copy(createTwinWalletState = state.createTwinWalletState?.copy( - step = CreateTwinWalletStep.ThirdStep - )) - CreateTwinWalletAction.LaunchSecondStep.Failure -> { - state - } - is CreateTwinWalletAction.LaunchThirdStep -> { - state - } - is CreateTwinWalletAction.LaunchThirdStep.Success -> { - state - } - CreateTwinWalletAction.LaunchThirdStep.Failure -> { - state - } - } - } - - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletState.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletState.kt deleted file mode 100644 index 0a7c6e04ee..0000000000 --- a/app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletState.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.tangem.tap.features.twins.redux - -import com.tangem.tap.domain.tasks.product.ScanResponse -import com.tangem.tap.domain.twins.TwinCardNumber - -data class CreateTwinWalletState( - val scanResponse: ScanResponse?, - val step: CreateTwinWalletStep = CreateTwinWalletStep.FirstStep, - val twinCardNumber: TwinCardNumber?, - val createTwinWallet: CreateTwinWallet?, - val showAlert: Boolean, - val allowRecreatingWallet: Boolean? = null -) - -enum class CreateTwinWalletStep { FirstStep, SecondStep, ThirdStep } - -enum class CreateTwinWallet { CreateWallet, RecreateWallet } diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsAction.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsAction.kt new file mode 100644 index 0000000000..cf7d2433dd --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsAction.kt @@ -0,0 +1,62 @@ +package com.tangem.tap.features.twins.redux + +import android.content.Context +import com.tangem.Message +import com.tangem.tap.common.redux.NotificationAction +import com.tangem.tap.domain.tasks.product.ScanResponse +import com.tangem.tap.domain.twins.TwinCardNumber +import com.tangem.wallet.R +import org.rekotlin.Action + +/** +[REDACTED_AUTHOR] + */ +sealed class TwinCardsAction : Action { + object ShowOnboarding : TwinCardsAction() + object SetOnboardingShown : TwinCardsAction() + data class SetTwinCard( + val number: TwinCardNumber, + val secondCardId: String?, + val isCreatingTwinCardsAllowed: Boolean, + ) : TwinCardsAction() + + sealed class CreateWallet : TwinCardsAction() { + data class Create( + val number: TwinCardNumber, + val createTwinWalletMode: CreateTwinWalletMode + ) : CreateWallet() + + object NotEmpty : CreateWallet(), NotificationAction { + override val messageResource = R.string.details_notification_erase_wallet_not_possible + } + + object ShowAlert : CreateWallet() + object HideAlert : CreateWallet() + object Proceed : CreateWallet() + + object Cancel : CreateWallet() { + object Confirm : CreateWallet() + } + + data class LaunchFirstStep( + val message: Message, val context: Context + ) : CreateWallet() { + object Success : CreateWallet() + object Failure : CreateWallet() + } + + data class LaunchSecondStep( + val initialMessage: Message, + val preparingMessage: Message, + val creatingWalletMessage: Message, + ) : CreateWallet() { + object Success : CreateWallet() + object Failure : CreateWallet() + } + + data class LaunchThirdStep(val message: Message) : CreateWallet() { + data class Success(val scanResponse: ScanResponse) : CreateWallet() + object Failure : CreateWallet() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsMiddleware.kt similarity index 61% rename from app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletMiddleware.kt rename to app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsMiddleware.kt index 69f72f798d..673e0616d5 100644 --- a/app/src/main/java/com/tangem/tap/features/twins/redux/CreateTwinWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsMiddleware.kt @@ -7,6 +7,7 @@ import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.extensions.toSendableAmounts import com.tangem.tap.domain.twins.TwinCardsManager +import com.tangem.tap.preferencesStorage import com.tangem.tap.scope import com.tangem.tap.store import kotlinx.coroutines.Dispatchers @@ -16,7 +17,7 @@ import org.rekotlin.Action import org.rekotlin.DispatchFunction import org.rekotlin.Middleware -class CreateTwinWalletMiddleware { +class TwinCardsMiddleware { companion object { val handler = twinsWalletMiddleware } @@ -34,40 +35,47 @@ private val twinsWalletMiddleware: Middleware = { dispatch, state -> private var twinsManager: TwinCardsManager? = null private fun handle(action: Action, dispatch: DispatchFunction) { - val action = action as? CreateTwinWalletAction ?: return + val action = action as? TwinCardsAction ?: return + val twinCardsState = store.state.twinCardsState when (action) { - is CreateTwinWalletAction.ShowWarning -> { - val wallet = store.state.detailsState.wallets.firstOrNull() + is TwinCardsAction.SetTwinCard -> { + val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown() + if (showOnboarding) store.dispatch(TwinCardsAction.ShowOnboarding) + } + TwinCardsAction.SetOnboardingShown -> { + preferencesStorage.saveTwinsOnboardingShown() + } + is TwinCardsAction.CreateWallet.Create -> { + val wallet = store.state.walletState.walletManagers.map { it.wallet }.firstOrNull() if (wallet == null) { store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning)) return } val notEmpty = wallet.recentTransactions.isNotEmpty() || wallet.amounts.toSendableAmounts().isNotEmpty() if (notEmpty) { - store.dispatch(CreateTwinWalletAction.NotEmpty) + store.dispatch(TwinCardsAction.CreateWallet.NotEmpty) } else { store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning)) } } - is CreateTwinWalletAction.Proceed -> + is TwinCardsAction.CreateWallet.Proceed -> { store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWallet)) - is CreateTwinWalletAction.Cancel -> { - - val step = store.state.detailsState.createTwinWalletState?.step - if (step != null && step != CreateTwinWalletStep.FirstStep - ) { - store.dispatch(CreateTwinWalletAction.ShowAlert) + } + is TwinCardsAction.CreateWallet.Cancel -> { + val step = twinCardsState.createWalletState?.step + if (step != null && step != CreateTwinWalletStep.FirstStep) { + store.dispatch(TwinCardsAction.CreateWallet.ShowAlert) } else { twinsManager = null store.dispatch(NavigationAction.PopBackTo()) } } - is CreateTwinWalletAction.Cancel.Confirm -> { + is TwinCardsAction.CreateWallet.Cancel.Confirm -> { twinsManager = null store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) } - is CreateTwinWalletAction.LaunchFirstStep -> { + is TwinCardsAction.CreateWallet.LaunchFirstStep -> { store.state.globalState.scanResponse?.let { twinsManager = TwinCardsManager(it, action.context) } @@ -76,62 +84,59 @@ private fun handle(action: Action, dispatch: DispatchFunction) { withContext(Dispatchers.Main) { when (result) { SimpleResult.Success -> - store.dispatch(CreateTwinWalletAction.LaunchFirstStep.Success) + store.dispatch(TwinCardsAction.CreateWallet.LaunchFirstStep.Success) is SimpleResult.Failure -> - store.dispatch(CreateTwinWalletAction.LaunchFirstStep.Failure) + store.dispatch(TwinCardsAction.CreateWallet.LaunchFirstStep.Failure) } } } } - CreateTwinWalletAction.LaunchFirstStep.Success -> { + TwinCardsAction.CreateWallet.LaunchFirstStep.Success -> { } - CreateTwinWalletAction.LaunchFirstStep.Failure -> { + TwinCardsAction.CreateWallet.LaunchFirstStep.Failure -> { } - is CreateTwinWalletAction.LaunchSecondStep -> + is TwinCardsAction.CreateWallet.LaunchSecondStep -> scope.launch { val result = twinsManager?.createSecondWallet(action.initialMessage, action.preparingMessage, action.creatingWalletMessage) withContext(Dispatchers.Main) { when (result) { SimpleResult.Success -> - store.dispatch(CreateTwinWalletAction.LaunchSecondStep.Success) + store.dispatch(TwinCardsAction.CreateWallet.LaunchSecondStep.Success) is SimpleResult.Failure -> - store.dispatch(CreateTwinWalletAction.LaunchSecondStep.Failure) + store.dispatch(TwinCardsAction.CreateWallet.LaunchSecondStep.Failure) } } } - CreateTwinWalletAction.LaunchSecondStep.Success -> { + TwinCardsAction.CreateWallet.LaunchSecondStep.Success -> { } - CreateTwinWalletAction.LaunchSecondStep.Failure -> { + TwinCardsAction.CreateWallet.LaunchSecondStep.Failure -> { } - is CreateTwinWalletAction.LaunchThirdStep -> { + is TwinCardsAction.CreateWallet.LaunchThirdStep -> { scope.launch { val result = twinsManager?.complete(action.message) withContext(Dispatchers.Main) { when (result) { is Result.Success -> - store.dispatch( - CreateTwinWalletAction - .LaunchThirdStep.Success(result.data) - ) + store.dispatch(TwinCardsAction.CreateWallet.LaunchThirdStep.Success(result.data)) is Result.Failure -> - store.dispatch(CreateTwinWalletAction.LaunchThirdStep.Failure) + store.dispatch(TwinCardsAction.CreateWallet.LaunchThirdStep.Failure) } } } } - is CreateTwinWalletAction.LaunchThirdStep.Success -> { + is TwinCardsAction.CreateWallet.LaunchThirdStep.Success -> { scope.launch { store.state.globalState.tapWalletManager.onCardScanned(action.scanResponse) } store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) } - CreateTwinWalletAction.LaunchThirdStep.Failure -> { + TwinCardsAction.CreateWallet.LaunchThirdStep.Failure -> { } } diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsReducer.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsReducer.kt new file mode 100644 index 0000000000..6f9e3a6f57 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsReducer.kt @@ -0,0 +1,63 @@ +package com.tangem.tap.features.twins.redux + +import com.tangem.tap.common.redux.AppState +import org.rekotlin.Action + +class TwinCardsReducer { + companion object { + fun reduce(action: Action, state: AppState): TwinCardsState = internalReduce(action, state) + } +} + +private fun internalReduce(action: Action, state: AppState): TwinCardsState { + if (action !is TwinCardsAction) return state.twinCardsState + + val twinCardsState = state.twinCardsState + return when (action) { + is TwinCardsAction.SetTwinCard -> { + twinCardsState.copy( + cardNumber = action.number, + secondCardId = action.secondCardId, + showTwinOnboarding = state.twinCardsState.showTwinOnboarding, + isCreatingTwinCardsAllowed = action.isCreatingTwinCardsAllowed, + ) + } + is TwinCardsAction.ShowOnboarding -> { + state.twinCardsState.copy(showTwinOnboarding = true) + } + is TwinCardsAction.SetOnboardingShown -> { + twinCardsState.copy(showTwinOnboarding = false) + } + is TwinCardsAction.CreateWallet.Create -> { + val prevState = twinCardsState.createWalletState + twinCardsState.copy(createWalletState = CreateTwinWalletState( + prevState?.scanResponse, + prevState?.number ?: action.number, + prevState?.mode ?: action.createTwinWalletMode, + )) + } + TwinCardsAction.CreateWallet.LaunchFirstStep.Success -> { + twinCardsState.copy(createWalletState = twinCardsState.createWalletState?.copy( + step = CreateTwinWalletStep.SecondStep + )) + } + TwinCardsAction.CreateWallet.LaunchSecondStep.Success -> + twinCardsState.copy(createWalletState = twinCardsState.createWalletState?.copy( + step = CreateTwinWalletStep.ThirdStep + )) + TwinCardsAction.CreateWallet.ShowAlert -> twinCardsState + TwinCardsAction.CreateWallet.HideAlert -> twinCardsState + is TwinCardsAction.CreateWallet.Proceed -> twinCardsState + TwinCardsAction.CreateWallet.NotEmpty -> twinCardsState + TwinCardsAction.CreateWallet.Cancel -> twinCardsState + TwinCardsAction.CreateWallet.Cancel.Confirm -> twinCardsState + is TwinCardsAction.CreateWallet.LaunchFirstStep -> twinCardsState + TwinCardsAction.CreateWallet.LaunchFirstStep.Failure -> twinCardsState + is TwinCardsAction.CreateWallet.LaunchSecondStep -> twinCardsState + TwinCardsAction.CreateWallet.LaunchSecondStep.Failure -> twinCardsState + is TwinCardsAction.CreateWallet.LaunchThirdStep -> twinCardsState + is TwinCardsAction.CreateWallet.LaunchThirdStep.Success -> twinCardsState + TwinCardsAction.CreateWallet.LaunchThirdStep.Failure -> twinCardsState + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsState.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsState.kt new file mode 100644 index 0000000000..49a43b9d93 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsState.kt @@ -0,0 +1,34 @@ +package com.tangem.tap.features.twins.redux + +import com.tangem.tap.domain.tasks.product.ScanResponse +import com.tangem.tap.domain.twins.TwinCardNumber +import org.rekotlin.StateType + +/** +[REDACTED_AUTHOR] + */ +data class TwinCardsState( + // if cardNumber is not set -> the scanned card is not Twin + val cardNumber: TwinCardNumber? = null, + val secondCardId: String? = null, + val showTwinOnboarding: Boolean = false, + val isCreatingTwinCardsAllowed: Boolean = false, + val createWalletState: CreateTwinWalletState? = null +) : StateType { + val isReadyToUse: Boolean + get() = cardNumber != null +} + +data class CreateTwinWalletState( + val scanResponse: ScanResponse?, + val number: TwinCardNumber = TwinCardNumber.First, + val mode: CreateTwinWalletMode = CreateTwinWalletMode.CreateWallet, + val step: CreateTwinWalletStep = CreateTwinWalletStep.FirstStep, +) { + val showAlert: Boolean + get() = mode != CreateTwinWalletMode.RecreateWallet +} + +enum class CreateTwinWalletStep { FirstStep, SecondStep, ThirdStep } + +enum class CreateTwinWalletMode { CreateWallet, RecreateWallet } diff --git a/app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt index 8c4b950a0d..0c23906aaf 100644 --- a/app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt @@ -11,10 +11,10 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.squareup.picasso.Picasso import com.tangem.Message import com.tangem.tap.domain.twins.TwinCardNumber -import com.tangem.tap.features.details.redux.DetailsState -import com.tangem.tap.features.twins.redux.CreateTwinWallet -import com.tangem.tap.features.twins.redux.CreateTwinWalletAction +import com.tangem.tap.features.twins.redux.CreateTwinWalletMode import com.tangem.tap.features.twins.redux.CreateTwinWalletStep +import com.tangem.tap.features.twins.redux.TwinCardsAction +import com.tangem.tap.features.twins.redux.TwinCardsState import com.tangem.tap.features.wallet.redux.Artwork import com.tangem.tap.store import com.tangem.wallet.R @@ -22,7 +22,7 @@ import kotlinx.android.synthetic.main.fragment_twin_cards_create.* import org.rekotlin.StoreSubscriber class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), - StoreSubscriber { + StoreSubscriber { private var dialog: Dialog? = null @@ -30,7 +30,7 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), super.onCreate(savedInstanceState) activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { - store.dispatch(CreateTwinWalletAction.Cancel) + store.dispatch(TwinCardsAction.CreateWallet.Cancel) } }) val inflater = TransitionInflater.from(requireContext()) @@ -42,8 +42,8 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), super.onStart() store.subscribe(this) { state -> state.skipRepeats { oldState, newState -> - oldState.detailsState == newState.detailsState - }.select { it.detailsState } + oldState.twinCardsState == newState.twinCardsState + }.select { it.twinCardsState } } } @@ -56,7 +56,7 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), super.onViewCreated(view, savedInstanceState) toolbar.setNavigationOnClickListener { - store.dispatch(CreateTwinWalletAction.Cancel) + store.dispatch(TwinCardsAction.CreateWallet.Cancel) } Picasso.get() @@ -73,21 +73,21 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), } - override fun newState(state: DetailsState) { + override fun newState(state: TwinCardsState) { if (activity == null) return - toolbar.title = when (state.createTwinWalletState?.createTwinWallet) { - CreateTwinWallet.CreateWallet -> getText(R.string.wallet_button_create_wallet) - CreateTwinWallet.RecreateWallet, null -> getText(R.string.details_twins_recreate_toolbar) + toolbar.title = when (state.createWalletState?.mode) { + CreateTwinWalletMode.CreateWallet -> getText(R.string.wallet_button_create_wallet) + CreateTwinWalletMode.RecreateWallet, null -> getText(R.string.details_twins_recreate_toolbar) } val selectedColor = getColor(requireContext(), R.color.colorSecondary) val defaultColor = getColor(requireContext(), R.color.blue_pale) - val twinCardNumber = state.createTwinWalletState?.twinCardNumber ?: TwinCardNumber.First + val twinCardNumber = state.createWalletState?.number ?: TwinCardNumber.First - val cardNumber = when (state.createTwinWalletState?.step) { + val cardNumber = when (state.createWalletState?.step) { CreateTwinWalletStep.FirstStep -> { val twinCardNumberString = twinCardNumber.number.toString() tv_step_number.text = @@ -96,7 +96,7 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), v_step_2.setBackgroundColor(defaultColor) v_step_3.setBackgroundColor(defaultColor) btn_tap.setOnClickListener { - store.dispatch(CreateTwinWalletAction.LaunchFirstStep( + store.dispatch(TwinCardsAction.CreateWallet.LaunchFirstStep( Message(getString( R.string.details_twins_recreate_title_format, twinCardNumberString @@ -117,7 +117,7 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), v_step_2.setBackgroundColor(selectedColor) v_step_3.setBackgroundColor(defaultColor) btn_tap.setOnClickListener { - store.dispatch(CreateTwinWalletAction.LaunchSecondStep( + store.dispatch(TwinCardsAction.CreateWallet.LaunchSecondStep( Message(getString(R.string.details_twins_recreate_title_format, twinCardNumberString)), Message(getString(R.string.details_twins_recreate_title_preparing)), Message(getString(R.string.details_twins_recreate_title_creating_wallet)), @@ -135,7 +135,7 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), v_step_2.setBackgroundColor(selectedColor) v_step_3.setBackgroundColor(selectedColor) btn_tap.setOnClickListener { - store.dispatch(CreateTwinWalletAction.LaunchThirdStep( + store.dispatch(TwinCardsAction.CreateWallet.LaunchThirdStep( Message(getString( R.string.details_twins_recreate_title_format, twinCardNumberString) ) @@ -149,18 +149,18 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), btn_tap.text = getString(R.string.details_twins_recreate_button_format, cardNumber) tv_twin_title.text = getString(R.string.details_twins_recreate_title_format, cardNumber) - if (state.createTwinWalletState?.showAlert == true) { + if (state.createWalletState?.showAlert == true) { if (dialog == null) { dialog = MaterialAlertDialogBuilder(requireContext()) .setMessage(R.string.details_twins_recreate_alert) .setPositiveButton(R.string.common_ok) { _, _ -> - store.dispatch(CreateTwinWalletAction.Cancel.Confirm) + store.dispatch(TwinCardsAction.CreateWallet.Cancel.Confirm) } .setNegativeButton(R.string.common_cancel) { _, _ -> dialog?.cancel() } .setOnCancelListener { - store.dispatch(CreateTwinWalletAction.HideAlert) + store.dispatch(TwinCardsAction.CreateWallet.HideAlert) } .create() dialog?.show() diff --git a/app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt b/app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt index a1103c5bc7..fefff8729c 100644 --- a/app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt @@ -7,8 +7,8 @@ import androidx.fragment.app.Fragment import androidx.transition.TransitionInflater import com.squareup.picasso.Picasso import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.features.twins.redux.CreateTwinWallet -import com.tangem.tap.features.twins.redux.CreateTwinWalletAction +import com.tangem.tap.features.twins.redux.CreateTwinWalletMode +import com.tangem.tap.features.twins.redux.TwinCardsAction import com.tangem.tap.features.wallet.redux.Artwork import com.tangem.tap.store import com.tangem.wallet.R @@ -31,15 +31,15 @@ class TwinWalletWarningFragment : Fragment(R.layout.fragment_twin_cards_warning) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val createTwinWallet = store.state.detailsState.createTwinWalletState?.createTwinWallet - if (createTwinWallet == CreateTwinWallet.CreateWallet) { + val createTwinWallet = store.state.twinCardsState.createWalletState?.mode + if (createTwinWallet == CreateTwinWalletMode.CreateWallet) { tv_twin_cards_description.text = getText(R.string.details_twins_recreate_subtitle) - } else if (createTwinWallet == CreateTwinWallet.RecreateWallet) { + } else if (createTwinWallet == CreateTwinWalletMode.RecreateWallet) { tv_twin_cards_description.text = getText(R.string.details_twins_recreate_warning) } btn_cancel.setOnClickListener { store.dispatch(NavigationAction.PopBackTo()) } - btn_start.setOnClickListener { store.dispatch(CreateTwinWalletAction.Proceed) } + btn_start.setOnClickListener { store.dispatch(TwinCardsAction.CreateWallet.Proceed) } Picasso.get() .load(Artwork.TWIN_CARD_1) .placeholder(R.drawable.card_placeholder) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt index 55e7fb4f66..ae0a273c14 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt @@ -9,7 +9,6 @@ import com.tangem.tap.common.redux.NotificationAction import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.tokens.CardCurrencies -import com.tangem.tap.domain.twins.TwinCardNumber import com.tangem.wallet.R import org.rekotlin.Action import java.math.BigDecimal @@ -147,12 +146,4 @@ sealed class WalletAction : Action { data class ChangeSelectedAddress(val type: AddressType) : WalletAction() - sealed class TwinsAction : WalletAction() { - object ShowOnboarding : TwinsAction() - object SetOnboardingShown : TwinsAction() - data class SetTwinCard( - val secondCardId: String?, val number: TwinCardNumber, - val isCreatingTwinCardsAllowed: Boolean, - ) : TwinsAction() - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt index 6c3db01f3c..aa6094f26c 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt @@ -12,39 +12,49 @@ import com.tangem.tap.common.toggleWidget.WidgetState import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.extensions.toSendableAmounts import com.tangem.tap.domain.topup.TradeCryptoHelper -import com.tangem.tap.domain.twins.TwinCardNumber +import com.tangem.tap.features.twins.redux.TwinCardsState import com.tangem.tap.features.wallet.models.PendingTransaction import com.tangem.tap.features.wallet.models.toPendingTransactions import com.tangem.tap.features.wallet.models.toPendingTransactionsForToken +import com.tangem.tap.features.wallet.ui.BalanceStatus import com.tangem.tap.features.wallet.ui.BalanceWidgetData import com.tangem.tap.store import org.rekotlin.StateType import java.math.BigDecimal +import kotlin.properties.ReadOnlyProperty data class WalletState( - val state: ProgressState = ProgressState.Done, - val error: ErrorType? = null, - val cardImage: Artwork? = null, - val hashesCountVerified: Boolean? = null, - val walletDialog: StateDialog? = null, - val twinCardsState: TwinCardsState? = null, - val mainWarningsList: List = mutableListOf(), - val wallets: List = emptyList(), - val walletManagers: List = emptyList(), - val isMultiwalletAllowed: Boolean = false, - val cardCurrency: CryptoCurrencyName? = null, - val selectedWallet: Currency? = null, - val primaryBlockchain: Blockchain? = null, - val primaryToken: Token? = null, - val isTestnet: Boolean = false, - val tradeCryptoAllowed: TradeCryptoAvailability = TradeCryptoAvailability() + val state: ProgressState = ProgressState.Done, + val error: ErrorType? = null, + val cardImage: Artwork? = null, + val hashesCountVerified: Boolean? = null, + val walletDialog: StateDialog? = null, + val mainWarningsList: List = mutableListOf(), + val wallets: List = emptyList(), + val walletManagers: List = emptyList(), + val isMultiwalletAllowed: Boolean = false, + val cardCurrency: CryptoCurrencyName? = null, + val selectedWallet: Currency? = null, + val primaryBlockchain: Blockchain? = null, + val primaryToken: Token? = null, + val isTestnet: Boolean = false, + val tradeCryptoAllowed: TradeCryptoAvailability = TradeCryptoAvailability() ) : StateType { + // if you do not delegate - the application crashes on startup, + // because twinCardsState has not been created yet + val twinCardsState: TwinCardsState by ReadOnlyProperty { thisRef, property -> + store.state.twinCardsState + } + + val isTangemTwins: Boolean + get() = store.state.globalState.scanResponse?.isTangemTwins() == true + val primaryWallet = if (wallets.isNotEmpty()) wallets[0] else null val shouldShowDetails: Boolean = - primaryWallet?.currencyData?.status != com.tangem.tap.features.wallet.ui.BalanceStatus.EmptyCard && - primaryWallet?.currencyData?.status != com.tangem.tap.features.wallet.ui.BalanceStatus.UnknownBlockchain + primaryWallet?.currencyData?.status != BalanceStatus.EmptyCard && + primaryWallet?.currencyData?.status != BalanceStatus.UnknownBlockchain val blockchains: List get() = walletManagers.map { it.wallet.blockchain } @@ -57,16 +67,16 @@ data class WalletState( return walletManagers.find { it.wallet.blockchain == token.blockchain } } - fun getWalletManager(currency: Currency?) : WalletManager? { + fun getWalletManager(currency: Currency?): WalletManager? { if (currency?.blockchain == null) return null return walletManagers.find { it.wallet.blockchain == currency.blockchain } } - fun getWalletManager(blockchain: Blockchain) : WalletManager? { + fun getWalletManager(blockchain: Blockchain): WalletManager? { return walletManagers.find { it.wallet.blockchain == blockchain } } - fun getWalletData(currency: Currency?) : WalletData? { + fun getWalletData(currency: Currency?): WalletData? { if (currency == null) return null return wallets.find { it.currency == currency } } @@ -93,7 +103,7 @@ data class WalletState( ?: return true if (walletData.currency is Currency.Blockchain && - walletManager.cardTokens.isNotEmpty() + walletManager.cardTokens.isNotEmpty() ) { return false } @@ -105,9 +115,9 @@ data class WalletState( wallet.amounts.toSendableAmounts().isEmpty() } else if (walletData.currency is Currency.Token) ( return wallet.recentTransactions.toPendingTransactionsForToken( - walletData.currency.token, wallet.address).isEmpty() + walletData.currency.token, wallet.address).isEmpty() && wallet.amounts[AmountType.Token(token = walletData.currency.token)] - ?.isAboveZero() != true + ?.isAboveZero() != true ) } return false @@ -137,9 +147,9 @@ data class WalletState( } } -sealed class WalletDialog: StateDialog { +sealed class WalletDialog : StateDialog { data class QrDialog( - val qrCode: Bitmap?, val shareUrl: String?, val currencyName: CryptoCurrencyName? + val qrCode: Bitmap?, val shareUrl: String?, val currencyName: CryptoCurrencyName? ) : WalletDialog() data class SelectAmountToSendDialog(val amounts: List?) : WalletDialog() @@ -147,7 +157,7 @@ sealed class WalletDialog: StateDialog { object ChooseTradeActionDialog : WalletDialog() } -enum class ProgressState: WidgetState { Loading, Done, Error } +enum class ProgressState : WidgetState { Loading, Done, Error } enum class ErrorType { NoInternetConnection } @@ -157,22 +167,22 @@ sealed class WalletMainButton(enabled: Boolean) : Button(enabled) { } data class WalletAddresses( - val selectedAddress: AddressData, - val list: List + val selectedAddress: AddressData, + val list: List ) data class AddressData( - val address: String, - val type: AddressType, - val shareUrl: String, - val exploreUrl: String, + val address: String, + val type: AddressType, + val shareUrl: String, + val exploreUrl: String, ) { val qrCode: Bitmap by lazy { shareUrl.toQrCode() } } data class Artwork( - val artworkId: String, - val artwork: Bitmap? = null + val artworkId: String, + val artwork: Bitmap? = null ) { companion object { const val DEFAULT_IMG_URL = "https://app.tangem.com/cards/card_default.png" @@ -198,13 +208,6 @@ data class TradeCryptoAvailability( val availableToSell: Set = TradeCryptoHelper.AVAILABLE_TO_SELL, ) -data class TwinCardsState( - val secondCardId: String?, - val cardNumber: TwinCardNumber?, - val showTwinOnboarding: Boolean, - val isCreatingTwinCardsAllowed: Boolean -) - data class WalletData( val pendingTransactions: List = emptyList(), val hashesCountVerified: Boolean? = null, diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TwinsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TwinsMiddleware.kt deleted file mode 100644 index 88ba60d1d1..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TwinsMiddleware.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.tangem.tap.features.wallet.redux.middlewares - -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.preferencesStorage -import com.tangem.tap.store - -class TwinsMiddleware { - fun handle(action: WalletAction.TwinsAction) { - when (action) { - is WalletAction.TwinsAction.SetTwinCard -> { - val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown() - if (showOnboarding) store.dispatch(WalletAction.TwinsAction.ShowOnboarding) - } - WalletAction.TwinsAction.SetOnboardingShown -> { - preferencesStorage.saveTwinsOnboardingShown() - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index 5f0ee586c7..61b4423e33 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -17,11 +17,10 @@ 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.extensions.toSendableAmounts -import com.tangem.tap.domain.twins.TwinsHelper import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.send.redux.PrepareSendScreen -import com.tangem.tap.features.twins.redux.CreateTwinWallet -import com.tangem.tap.features.twins.redux.CreateTwinWalletAction +import com.tangem.tap.features.twins.redux.CreateTwinWalletMode +import com.tangem.tap.features.twins.redux.TwinCardsAction import com.tangem.tap.features.wallet.redux.* import com.tangem.tap.network.NetworkStateChanged import kotlinx.coroutines.launch @@ -31,7 +30,6 @@ import org.rekotlin.Middleware class WalletMiddleware { private val tradeCryptoMiddleware = TradeCryptoMiddleware() - private val twinsMiddleware = TwinsMiddleware() private val warningsMiddleware = WarningsMiddleware() private val multiWalletMiddleware = MultiWalletMiddleware() @@ -50,7 +48,6 @@ class WalletMiddleware { when (action) { is WalletAction.TradeCryptoAction -> tradeCryptoMiddleware.handle(action) - is WalletAction.TwinsAction -> twinsMiddleware.handle(action) is WalletAction.Warnings -> warningsMiddleware.handle(action, globalState) is WalletAction.MultiWallet -> multiWalletMiddleware.handle(action, walletState, globalState) is WalletAction.LoadWallet -> { @@ -99,12 +96,11 @@ class WalletMiddleware { } } is WalletAction.CreateWallet -> { - if (walletState.twinCardsState != null) { - val cardId = globalState.scanResponse?.card?.cardId - val twinCardNumber = cardId?.let { TwinsHelper.getTwinCardNumber(it) } - store.dispatch( - CreateTwinWalletAction.ShowWarning(twinCardNumber, CreateTwinWallet.CreateWallet) - ) + if (walletState.isTangemTwins) { + store.dispatch(TwinCardsAction.CreateWallet.Create( + walletState.twinCardsState.cardNumber!!, + CreateTwinWalletMode.CreateWallet + )) } else { scope.launch { val result = tangemSdkManager.createWallet( diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/TwinsReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/TwinsReducer.kt deleted file mode 100644 index 79025ca6cc..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/TwinsReducer.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.tangem.tap.features.wallet.redux.reducers - -import com.tangem.tap.features.wallet.redux.TwinCardsState -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.wallet.redux.WalletState - -class TwinsReducer { - fun reduce(action: WalletAction.TwinsAction, state: WalletState): WalletState { - return when (action) { - is WalletAction.TwinsAction.SetTwinCard -> { - state.copy( - twinCardsState = TwinCardsState( - secondCardId = action.secondCardId, - cardNumber = action.number, - showTwinOnboarding = state.twinCardsState?.showTwinOnboarding - ?: false, - isCreatingTwinCardsAllowed = action.isCreatingTwinCardsAllowed - ) - ) - } - is WalletAction.TwinsAction.ShowOnboarding -> { - state.copy( - twinCardsState = state.twinCardsState?.copy(showTwinOnboarding = true) - ?: TwinCardsState(null, null, - showTwinOnboarding = true, - isCreatingTwinCardsAllowed = false) - ) - } - is WalletAction.TwinsAction.SetOnboardingShown -> { - state.copy( - twinCardsState = state.twinCardsState?.copy(showTwinOnboarding = false) - ) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt index d999216bba..8e0e9df126 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt @@ -25,7 +25,6 @@ class WalletReducer { private fun internalReduce(action: Action, state: AppState): WalletState { val multiWalletReducer = MultiWalletReducer() - val twinsReducer = TwinsReducer() val onWalletLoadedReducer = OnWalletLoadedReducer() if (action !is WalletAction) return state.walletState @@ -34,15 +33,12 @@ private fun internalReduce(action: Action, state: AppState): WalletState { when (action) { is WalletAction.Warnings -> newState = handleCheckSignedHashesActions(action, newState) - is WalletAction.TwinsAction -> newState = twinsReducer.reduce(action, newState) is WalletAction.MultiWallet -> newState = multiWalletReducer.reduce(action, newState) is WalletAction.ResetState -> newState = WalletState() is WalletAction.SetIfTestnetCard -> newState = newState.copy(isTestnet = action.isTestnet) is WalletAction.EmptyWallet -> { - val creatingWalletAllowed = !(newState.twinCardsState != null && - newState.twinCardsState?.isCreatingTwinCardsAllowed != true) - + val creatingWalletAllowed = state.twinCardsState.isCreatingTwinCardsAllowed newState = newState.copy( state = ProgressState.Done, wallets = listOf( @@ -215,7 +211,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState { newState = setNewFiatRate(action.fiatRate, state.globalState.appCurrency, newState) is WalletAction.LoadArtwork -> { val artworkUrl = action.card.getArtworkUrl(action.artworkId) - ?: when (newState.twinCardsState?.cardNumber) { + ?: when (state.twinCardsState.cardNumber) { TwinCardNumber.First -> Artwork.TWIN_CARD_1 TwinCardNumber.Second -> Artwork.TWIN_CARD_2 else -> Artwork.DEFAULT_IMG_URL diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index e8f2a250fd..fd65f1b619 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt @@ -135,8 +135,8 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber store.dispatch(DetailsAction.PrepareScreen( - scanNoteResponse.card, scanNoteResponse, - store.state.walletState.walletManagers.map { it.wallet }, - store.state.globalState.configManager?.config?.isCreatingTwinCardsAllowed, - CardTou(), - store.state.globalState.appCurrency + scanNoteResponse, + store.state.walletState.walletManagers.map { it.wallet }, + CardTou(), + store.state.globalState.appCurrency )) store.dispatch(NavigationAction.NavigateTo(AppScreen.Details)) true diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt index d2e4e77cc5..3c760f3e6e 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt @@ -10,6 +10,7 @@ import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.twins.TwinCardNumber +import com.tangem.tap.features.twins.redux.TwinCardsState import com.tangem.tap.features.wallet.models.PendingTransaction import com.tangem.tap.features.wallet.redux.* import com.tangem.tap.features.wallet.ui.BalanceWidget @@ -71,7 +72,7 @@ class SingleWalletView : WalletView { state.primaryWallet ?: return setupTwinCards(state.twinCardsState, fragment) - setupButtons(state.primaryWallet, state.twinCardsState != null, fragment) + setupButtons(state.primaryWallet, state.isTangemTwins, fragment) setupAddressCard(state.primaryWallet, fragment) showPendingTransactionsIfPresent(state.primaryWallet.pendingTransactions) setupBalance(state, state.primaryWallet) @@ -87,7 +88,7 @@ class SingleWalletView : WalletView { private fun setupBalance(state: WalletState, primaryWallet: WalletData) { fragment?.apply { this.l_balance.show() - BalanceWidget(this, primaryWallet.currencyData, state.twinCardsState != null).setup() + BalanceWidget(this, primaryWallet.currencyData, state.isTangemTwins).setup() } }