Updated on 2026-08-14

This commit is contained in:
Tangem 2021-10-22 05:54:34 +03:00
parent 8ce2b8d52b
commit f34aeba4ed
18 changed files with 222 additions and 199 deletions

View file

@ -7,6 +7,8 @@ import com.tangem.tap.common.redux.global.GlobalState
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog
import com.tangem.tap.features.details.ui.walletconnect.dialogs.*
import com.tangem.tap.features.onboarding.AddressInfoBottomSheetDialog
import com.tangem.tap.features.twins.redux.TwinCardsAction
import com.tangem.tap.features.twins.ui.dialog.CreateWalletInterruptDialog
import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog
import com.tangem.tap.store
import com.tangem.wallet.R
@ -43,24 +45,25 @@ class DialogManager : StoreSubscriber<GlobalState> {
dialog = when (state.dialog) {
is AppDialog.ScanFailsDialog -> ScanFailsDialog.create(context)
is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context)
is TwinCardsAction.Wallet.InterruptDialog -> CreateWalletInterruptDialog.create(state.dialog, context)
is WalletConnectDialog.UnsupportedCard ->
SimpleAlertDialog.create(
titleRes = R.string.wallet_connect,
messageRes = R.string.wallet_connect_scanner_error_no_ethereum_wallet,
context = context
titleRes = R.string.wallet_connect,
messageRes = R.string.wallet_connect_scanner_error_no_ethereum_wallet,
context = context
)
is WalletConnectDialog.OpeningSessionRejected -> {
SimpleAlertDialog.create(
titleRes = R.string.wallet_connect,
messageRes = R.string.wallet_connect_same_wcuri,
context = context
titleRes = R.string.wallet_connect,
messageRes = R.string.wallet_connect_same_wcuri,
context = context
)
}
is WalletConnectDialog.SessionTimeout -> {
SimpleAlertDialog.create(
titleRes = R.string.wallet_connect,
messageRes = R.string.wallet_connect_error_timeout,
context = context
titleRes = R.string.wallet_connect,
messageRes = R.string.wallet_connect_error_timeout,
context = context
)
}
is WalletConnectDialog.ApproveWcSession ->

View file

@ -16,9 +16,7 @@ import com.tangem.tap.domain.configurable.config.ConfigManager
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.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,20 +80,13 @@ class TapWalletManager {
FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.CARD_IS_SCANNED, data.card, data.walletData?.blockchain)
}
store.state.globalState.feedbackManager?.infoHolder?.setCardInfo(data.card)
val configManager = store.state.globalState.configManager
updateConfigManager(configManager, data)
updateConfigManager(data)
withContext(Dispatchers.Main) {
store.dispatch(WalletAction.ResetState)
store.dispatch(GlobalAction.SaveScanNoteResponse(data))
store.dispatch(WalletAction.SetIfTestnetCard(data.card.isTestCard))
store.dispatch(WalletAction.MultiWallet.SetIsMultiwalletAllowed(data.card.isMultiwalletAllowed))
if (data.isTangemTwins()) {
data.card.getTwinCardNumber()?.let {
val isCreatingTwinsAllowed = configManager?.config?.isCreatingTwinCardsAllowed ?: false
store.dispatch(TwinCardsAction.SetTwinCard(it, null, isCreatingTwinsAllowed))
}
}
val blockchain = data.getBlockchain()
if (blockchain == Blockchain.Ethereum ||
@ -106,7 +97,8 @@ class TapWalletManager {
}
}
private fun updateConfigManager(configManager: ConfigManager?, data: ScanResponse) {
private fun updateConfigManager(data: ScanResponse) {
val configManager = store.state.globalState.configManager
val blockchain = data.getBlockchain()
if (data.card.isStart2Coin) {
configManager?.turnOff(ConfigManager.isSendingToPayIdEnabled)

View file

@ -1,6 +1,5 @@
package com.tangem.tap.domain.twins
import android.content.Context
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Types
import com.tangem.Message
@ -12,24 +11,21 @@ import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toHexString
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
import com.tangem.tap.common.extensions.readAssetAsString
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.network.createMoshi
import com.tangem.tap.tangemSdkManager
class TwinCardsManager(private val scanResponse: ScanResponse, context: Context) {
class TwinCardsManager(private val scanResponse: ScanResponse, assetReader: AssetReader) {
private val currentCardId: String = scanResponse.card.cardId
// private val secondCardId: String? = TwinsHelper.getTwinsCardId(currentCardId)
// private val secondCardId: Strings? = TwinsHelper.getTwinsCardId(currentCardId)
private val secondCardId: String? = null
private var currentCardPublicKey: String? = null
private var secondCardPublicKey: String? = null
private val issuerKeyPair: KeyPair = getIssuerKeys(
context, scanResponse.card.issuer.publicKey.toHexString()
)
private val issuerKeyPair: KeyPair = getIssuerKeys(assetReader, scanResponse.card.issuer.publicKey.toHexString())
suspend fun createFirstWallet(message: Message): SimpleResult {
val response = tangemSdkManager.runTaskAsync(
@ -108,8 +104,8 @@ class TwinCardsManager(private val scanResponse: ScanResponse, context: Context)
}
companion object {
private fun getIssuerKeys(context: Context, publicKey: String): KeyPair {
val issuer = getIssuers(context).first { it.publicKey == publicKey }
private fun getIssuerKeys(reader: AssetReader, publicKey: String): KeyPair {
val issuer = getIssuers(reader).first { it.publicKey == publicKey }
return KeyPair(
publicKey = issuer.publicKey.hexToBytes(),
privateKey = issuer.privateKey.hexToBytes()
@ -122,13 +118,17 @@ class TwinCardsManager(private val scanResponse: ScanResponse, context: Context)
)
}
private fun getIssuers(context: Context): List<Issuer> {
val file = context.readAssetAsString("tangem-app-config/issuers")
private fun getIssuers(reader: AssetReader): List<Issuer> {
val file = reader.readAssetAsString("tangem-app-config/issuers")
return getAdapter().fromJson(file)!!
}
}
}
interface AssetReader {
fun readAssetAsString(name: String): String
}
private class Issuer(
val id: String,
val privateKey: String,

View file

@ -95,7 +95,7 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
tv_erase_wallet.show()
tv_erase_wallet.text = getText(R.string.details_row_title_twins_recreate)
tv_erase_wallet.setOnClickListener {
store.dispatch(TwinCardsAction.CreateWallet.Create(
store.dispatch(TwinCardsAction.Wallet.Create(
state.twinCardsState.cardNumber!!,
CreateTwinWalletMode.RecreateWallet
))

View file

@ -10,8 +10,10 @@ 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.twins.getTwinCardNumber
import com.tangem.tap.features.onboarding.OnboardingHelper
import com.tangem.tap.features.send.redux.states.ButtonState
import com.tangem.tap.features.twins.redux.TwinCardsAction
import com.tangem.tap.preferencesStorage
import com.tangem.tap.scope
import com.tangem.tap.store
@ -56,6 +58,14 @@ private fun handleReadCard() {
} else {
changeButtonState(ButtonState.PROGRESS)
store.dispatch(GlobalAction.ScanCard({ scanResponse ->
if (scanResponse.isTangemTwins()) {
scanResponse.card.getTwinCardNumber()?.let {
val configManager = store.state.globalState.configManager
val isCreatingTwinsAllowed = configManager?.config?.isCreatingTwinCardsAllowed ?: false
store.dispatch(TwinCardsAction.SetTwinCard(it, null, isCreatingTwinsAllowed))
}
}
if (OnboardingHelper.isOnboardingCase(scanResponse)) {
val navigateTo = OnboardingHelper.whereToNavigate(scanResponse)
store.dispatch(GlobalAction.Onboarding.Start(scanResponse))

View file

@ -19,7 +19,10 @@ import com.tangem.tap.common.postUi
import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget
import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
import com.tangem.tap.features.onboarding.products.note.redux.*
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteResources
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteStep
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_onboarding_main.*
@ -43,14 +46,14 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
toolbar.setTitle(R.string.onboarding_title)
btnRefreshBalanceWidget = RefreshBalanceWidget(onboarding_main_container)
val resources = AndroidResources(
OnboardingStringResources(
val resources = OnboardingNoteResources(
OnboardingNoteResources.Strings(
R.string.copy_toast_msg
)
)
store.dispatch(OnboardingNoteAction.LoadCardArtwork)
store.dispatch(OnboardingNoteAction.SetResources(resources))
store.dispatch(OnboardingNoteAction.LoadCardArtwork)
store.dispatch(OnboardingNoteAction.DetermineStepOfScreen)
}

View file

@ -10,7 +10,7 @@ import org.rekotlin.Action
*/
sealed class OnboardingNoteAction : Action {
// from user, ui
data class SetResources(val resources: AndroidResources) : OnboardingNoteAction()
data class SetResources(val resources: OnboardingNoteResources) : OnboardingNoteAction()
object LoadCardArtwork : OnboardingNoteAction()
object CreateWallet : OnboardingNoteAction()
object TopUp : OnboardingNoteAction()

View file

@ -10,7 +10,7 @@ import org.rekotlin.StateType
*/
data class OnboardingNoteState(
val walletManager: WalletManager? = null,
val resources: AndroidResources = AndroidResources(),
val resources: OnboardingNoteResources = OnboardingNoteResources(),
// UI
val cardArtwork: Artwork? = null,
val walletBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading(),
@ -27,10 +27,10 @@ enum class OnboardingNoteStep {
None, CreateWallet, TopUpWallet, Done
}
data class AndroidResources(
val strings: OnboardingStringResources = OnboardingStringResources()
)
data class OnboardingStringResources(
val addressWasCopied: Int = -1
)
data class OnboardingNoteResources(
val strings: Strings = Strings()
) {
data class Strings(
val addressWasCopied: Int = -1
)
}

View file

@ -1,62 +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.common.redux.StateDialog
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.domain.twins.AssetReader
import com.tangem.tap.domain.twins.TwinCardNumber
import com.tangem.wallet.R
import com.tangem.tap.domain.twins.TwinCardsManager
import org.rekotlin.Action
/**
[REDACTED_AUTHOR]
*/
sealed class TwinCardsAction : Action {
object ShowOnboarding : TwinCardsAction()
object SetOnboardingShown : TwinCardsAction()
data class SetResources(val resources: TwinCardsResources) : TwinCardsAction()
data class SetTwinCard(
val number: TwinCardNumber,
val secondCardId: String?,
val isCreatingTwinCardsAllowed: Boolean,
) : TwinCardsAction()
sealed class CreateWallet : TwinCardsAction() {
sealed class CardsManager {
data class Set(val manager: TwinCardsManager) : TwinCardsAction()
object Release : TwinCardsAction()
}
object ShowOnboarding : TwinCardsAction()
object SetOnboardingShown : TwinCardsAction()
object ProceedToCreateWallet : TwinCardsAction()
sealed class Wallet : TwinCardsAction() {
object HandleOnBackPressed : TwinCardsAction()
data class Create(
val number: TwinCardNumber,
val createTwinWalletMode: CreateTwinWalletMode
) : CreateWallet()
) : TwinCardsAction()
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 InterruptDialog(val interrupt: () -> Unit) : TwinCardsAction(), StateDialog
data class LaunchFirstStep(
val message: Message, val context: Context
) : CreateWallet() {
object Success : CreateWallet()
object Failure : CreateWallet()
val initialMessage: Message,
val reader: AssetReader
) : TwinCardsAction() {
object Success : TwinCardsAction()
}
data class LaunchSecondStep(
val initialMessage: Message,
val preparingMessage: Message,
val creatingWalletMessage: Message,
) : CreateWallet() {
object Success : CreateWallet()
object Failure : CreateWallet()
) : TwinCardsAction() {
object Success : TwinCardsAction()
}
data class LaunchThirdStep(val message: Message) : CreateWallet() {
data class Success(val scanResponse: ScanResponse) : CreateWallet()
object Failure : CreateWallet()
data class LaunchThirdStep(val message: Message) : TwinCardsAction() {
data class Success(val scanResponse: ScanResponse) : TwinCardsAction()
}
}
}

View file

@ -2,6 +2,8 @@ package com.tangem.tap.features.twins.redux
import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchNotification
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
@ -32,12 +34,11 @@ private val twinsWalletMiddleware: Middleware<AppState> = { dispatch, state ->
}
}
private var twinsManager: TwinCardsManager? = null
private fun handle(action: Action, dispatch: DispatchFunction) {
val action = action as? TwinCardsAction ?: return
val twinCardsState = store.state.twinCardsState
when (action) {
is TwinCardsAction.SetTwinCard -> {
val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown()
@ -46,7 +47,22 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
TwinCardsAction.SetOnboardingShown -> {
preferencesStorage.saveTwinsOnboardingShown()
}
is TwinCardsAction.CreateWallet.Create -> {
is TwinCardsAction.ProceedToCreateWallet -> {
store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWallet))
}
is TwinCardsAction.Wallet.HandleOnBackPressed -> {
if (twinCardsState.createWalletState?.showAlert == true) {
val stateDialog = TwinCardsAction.Wallet.InterruptDialog {
store.dispatch(TwinCardsAction.CardsManager.Release)
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
}
store.dispatchDialogShow(stateDialog)
} else {
store.dispatch(TwinCardsAction.CardsManager.Release)
store.dispatch(NavigationAction.PopBackTo())
}
}
is TwinCardsAction.Wallet.Create -> {
val wallet = store.state.walletState.walletManagers.map { it.wallet }.firstOrNull()
if (wallet == null) {
store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning))
@ -54,90 +70,70 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
}
val notEmpty = wallet.recentTransactions.isNotEmpty() || wallet.amounts.toSendableAmounts().isNotEmpty()
if (notEmpty) {
store.dispatch(TwinCardsAction.CreateWallet.NotEmpty)
store.dispatchNotification(twinCardsState.resources.strings.walletIsNotEmpty)
} else {
store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning))
}
}
is TwinCardsAction.CreateWallet.Proceed -> {
store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWallet))
}
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 TwinCardsAction.CreateWallet.Cancel.Confirm -> {
twinsManager = null
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
}
is TwinCardsAction.CreateWallet.LaunchFirstStep -> {
store.state.globalState.scanResponse?.let {
twinsManager = TwinCardsManager(it, action.context)
}
is TwinCardsAction.Wallet.LaunchFirstStep -> {
val scanResponse = store.state.globalState.scanResponse ?: return
val manager = TwinCardsManager(scanResponse, action.reader)
store.dispatch(TwinCardsAction.CardsManager.Set(manager))
scope.launch {
val result = twinsManager?.createFirstWallet(action.message)
val result = manager.createFirstWallet(action.initialMessage)
withContext(Dispatchers.Main) {
when (result) {
SimpleResult.Success ->
store.dispatch(TwinCardsAction.CreateWallet.LaunchFirstStep.Success)
is SimpleResult.Failure ->
store.dispatch(TwinCardsAction.CreateWallet.LaunchFirstStep.Failure)
SimpleResult.Success -> {
store.dispatch(TwinCardsAction.Wallet.LaunchFirstStep.Success)
}
is SimpleResult.Failure -> {
}
}
}
}
}
TwinCardsAction.CreateWallet.LaunchFirstStep.Success -> {
is TwinCardsAction.Wallet.LaunchSecondStep -> {
val manager = twinCardsState.twinCardsManager ?: return
}
TwinCardsAction.CreateWallet.LaunchFirstStep.Failure -> {
}
is TwinCardsAction.CreateWallet.LaunchSecondStep ->
scope.launch {
val result = twinsManager?.createSecondWallet(action.initialMessage,
val result = manager.createSecondWallet(action.initialMessage,
action.preparingMessage, action.creatingWalletMessage)
withContext(Dispatchers.Main) {
when (result) {
SimpleResult.Success ->
store.dispatch(TwinCardsAction.CreateWallet.LaunchSecondStep.Success)
is SimpleResult.Failure ->
store.dispatch(TwinCardsAction.CreateWallet.LaunchSecondStep.Failure)
SimpleResult.Success -> {
store.dispatch(TwinCardsAction.Wallet.LaunchSecondStep.Success)
}
is SimpleResult.Failure -> {
}
}
}
}
TwinCardsAction.CreateWallet.LaunchSecondStep.Success -> {
}
TwinCardsAction.CreateWallet.LaunchSecondStep.Failure -> {
is TwinCardsAction.Wallet.LaunchThirdStep -> {
val manager = twinCardsState.twinCardsManager ?: return
}
is TwinCardsAction.CreateWallet.LaunchThirdStep -> {
scope.launch {
val result = twinsManager?.complete(action.message)
val result = manager.complete(action.message)
withContext(Dispatchers.Main) {
when (result) {
is Result.Success ->
store.dispatch(TwinCardsAction.CreateWallet.LaunchThirdStep.Success(result.data))
is Result.Failure ->
store.dispatch(TwinCardsAction.CreateWallet.LaunchThirdStep.Failure)
is Result.Success -> {
store.dispatch(TwinCardsAction.Wallet.LaunchThirdStep.Success(result.data))
}
is Result.Failure -> {
}
}
}
}
}
is TwinCardsAction.CreateWallet.LaunchThirdStep.Success -> {
is TwinCardsAction.Wallet.LaunchThirdStep.Success -> {
scope.launch {
store.state.globalState.tapWalletManager.onCardScanned(action.scanResponse)
}
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
}
TwinCardsAction.CreateWallet.LaunchThirdStep.Failure -> {
}
}
}

View file

@ -12,52 +12,49 @@ class TwinCardsReducer {
private fun internalReduce(action: Action, state: AppState): TwinCardsState {
if (action !is TwinCardsAction) return state.twinCardsState
val twinCardsState = state.twinCardsState
val state = state.twinCardsState
return when (action) {
is TwinCardsAction.SetResources -> {
state.copy(resources = action.resources)
}
is TwinCardsAction.SetTwinCard -> {
twinCardsState.copy(
state.copy(
cardNumber = action.number,
secondCardId = action.secondCardId,
showTwinOnboarding = state.twinCardsState.showTwinOnboarding,
showTwinOnboarding = state.showTwinOnboarding,
isCreatingTwinCardsAllowed = action.isCreatingTwinCardsAllowed,
)
}
is TwinCardsAction.CardsManager.Set -> {
state.copy(twinCardsManager = action.manager)
}
TwinCardsAction.CardsManager.Release -> {
state.copy(twinCardsManager = null)
}
is TwinCardsAction.ShowOnboarding -> {
state.twinCardsState.copy(showTwinOnboarding = true)
state.copy(showTwinOnboarding = true)
}
is TwinCardsAction.SetOnboardingShown -> {
twinCardsState.copy(showTwinOnboarding = false)
state.copy(showTwinOnboarding = false)
}
is TwinCardsAction.CreateWallet.Create -> {
val prevState = twinCardsState.createWalletState
twinCardsState.copy(createWalletState = CreateTwinWalletState(
is TwinCardsAction.Wallet.Create -> {
val prevState = state.createWalletState
state.copy(createWalletState = CreateTwinWalletState(
prevState?.scanResponse,
prevState?.number ?: action.number,
prevState?.mode ?: action.createTwinWalletMode,
))
}
TwinCardsAction.CreateWallet.LaunchFirstStep.Success -> {
twinCardsState.copy(createWalletState = twinCardsState.createWalletState?.copy(
TwinCardsAction.Wallet.LaunchFirstStep.Success -> {
state.copy(createWalletState = state.createWalletState?.copy(
step = CreateTwinWalletStep.SecondStep
))
}
TwinCardsAction.CreateWallet.LaunchSecondStep.Success ->
twinCardsState.copy(createWalletState = twinCardsState.createWalletState?.copy(
TwinCardsAction.Wallet.LaunchSecondStep.Success ->
state.copy(createWalletState = state.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
else -> state
}
}

View file

@ -2,22 +2,22 @@ package com.tangem.tap.features.twins.redux
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.domain.twins.TwinCardNumber
import com.tangem.tap.domain.twins.TwinCardsManager
import org.rekotlin.StateType
/**
[REDACTED_AUTHOR]
*/
data class TwinCardsState(
val twinCardsManager: TwinCardsManager? = null,
// 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
}
val createWalletState: CreateTwinWalletState? = null,
val resources: TwinCardsResources = TwinCardsResources(),
) : StateType
data class CreateTwinWalletState(
val scanResponse: ScanResponse?,
@ -26,9 +26,18 @@ data class CreateTwinWalletState(
val step: CreateTwinWalletStep = CreateTwinWalletStep.FirstStep,
) {
val showAlert: Boolean
get() = mode != CreateTwinWalletMode.RecreateWallet
get() = step != CreateTwinWalletStep.FirstStep
}
enum class CreateTwinWalletStep { FirstStep, SecondStep, ThirdStep }
enum class CreateTwinWalletMode { CreateWallet, RecreateWallet }
data class TwinCardsResources(
val strings: Strings = Strings()
) {
//TODO: create global structure for StringRes. Set it from the MainActivity.
data class Strings(
val walletIsNotEmpty: Int = -1
)
}

View file

@ -1,20 +1,17 @@
package com.tangem.tap.features.twins.ui
import android.app.Dialog
import android.os.Bundle
import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.core.content.ContextCompat.getColor
import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.squareup.picasso.Picasso
import com.tangem.Message
import com.tangem.tap.common.extensions.readAssetAsString
import com.tangem.tap.domain.twins.AssetReader
import com.tangem.tap.domain.twins.TwinCardNumber
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.twins.redux.*
import com.tangem.tap.features.wallet.redux.Artwork
import com.tangem.tap.store
import com.tangem.wallet.R
@ -24,20 +21,35 @@ import org.rekotlin.StoreSubscriber
class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create),
StoreSubscriber<TwinCardsState> {
private var dialog: Dialog? = null
private val assetReader: AssetReader by lazy {
object : AssetReader {
override fun readAssetAsString(name: String): String = requireContext().readAssetAsString(name)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
store.dispatch(TwinCardsAction.CreateWallet.Cancel)
store.dispatch(TwinCardsAction.Wallet.HandleOnBackPressed)
}
})
initResources()
val inflater = TransitionInflater.from(requireContext())
enterTransition = inflater.inflateTransition(R.transition.slide_right)
exitTransition = inflater.inflateTransition(R.transition.fade)
}
private fun initResources() {
val resources = TwinCardsResources(
TwinCardsResources.Strings(
R.string.details_notification_erase_wallet_not_possible
)
)
store.dispatch(TwinCardsAction.SetResources(resources))
}
override fun onStart() {
super.onStart()
store.subscribe(this) { state ->
@ -55,9 +67,7 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create),
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
toolbar.setNavigationOnClickListener {
store.dispatch(TwinCardsAction.CreateWallet.Cancel)
}
toolbar.setNavigationOnClickListener { store.dispatch(TwinCardsAction.Wallet.HandleOnBackPressed) }
Picasso.get()
.load(Artwork.TWIN_CARD_1)
@ -96,12 +106,12 @@ class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create),
v_step_2.setBackgroundColor(defaultColor)
v_step_3.setBackgroundColor(defaultColor)
btn_tap.setOnClickListener {
store.dispatch(TwinCardsAction.CreateWallet.LaunchFirstStep(
store.dispatch(TwinCardsAction.Wallet.LaunchFirstStep(
Message(getString(
R.string.details_twins_recreate_title_format,
twinCardNumberString
)),
requireContext()
assetReader
))
}
btn_tap.text = getString(R.string.details_twins_recreate_button_format,
@ -117,7 +127,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(TwinCardsAction.CreateWallet.LaunchSecondStep(
store.dispatch(TwinCardsAction.Wallet.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 +145,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(TwinCardsAction.CreateWallet.LaunchThirdStep(
store.dispatch(TwinCardsAction.Wallet.LaunchThirdStep(
Message(getString(
R.string.details_twins_recreate_title_format, twinCardNumberString)
)
@ -148,27 +158,5 @@ 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.createWalletState?.showAlert == true) {
if (dialog == null) {
dialog = MaterialAlertDialogBuilder(requireContext())
.setMessage(R.string.details_twins_recreate_alert)
.setPositiveButton(R.string.common_ok) { _, _ ->
store.dispatch(TwinCardsAction.CreateWallet.Cancel.Confirm)
}
.setNegativeButton(R.string.common_cancel) { _, _ ->
dialog?.cancel()
}
.setOnCancelListener {
store.dispatch(TwinCardsAction.CreateWallet.HideAlert)
}
.create()
dialog?.show()
}
} else {
dialog?.cancel()
dialog = null
}
}
}

View file

@ -39,7 +39,7 @@ class TwinWalletWarningFragment : Fragment(R.layout.fragment_twin_cards_warning)
}
btn_cancel.setOnClickListener { store.dispatch(NavigationAction.PopBackTo()) }
btn_start.setOnClickListener { store.dispatch(TwinCardsAction.CreateWallet.Proceed) }
btn_start.setOnClickListener { store.dispatch(TwinCardsAction.ProceedToCreateWallet) }
Picasso.get()
.load(Artwork.TWIN_CARD_1)
.placeholder(R.drawable.card_placeholder)

View file

@ -0,0 +1,25 @@
package com.tangem.tap.features.twins.ui.dialog
import android.content.Context
import androidx.appcompat.app.AlertDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.tangem.tap.common.extensions.dispatchDialogHide
import com.tangem.tap.features.twins.redux.TwinCardsAction
import com.tangem.tap.store
import com.tangem.wallet.R
/**
[REDACTED_AUTHOR]
*/
class CreateWalletInterruptDialog {
companion object {
fun create(state: TwinCardsAction.Wallet.InterruptDialog, context: Context): AlertDialog {
return MaterialAlertDialogBuilder(context)
.setMessage(R.string.details_twins_recreate_alert)
.setPositiveButton(R.string.common_ok) { _, _ -> state.interrupt() }
.setNegativeButton(R.string.common_cancel) { _, _ -> }
.setOnDismissListener { store.dispatchDialogHide() }
.create()
}
}
}

View file

@ -65,5 +65,5 @@ fun Wallet.getPendingTransactions(): List<PendingTransaction> {
}
fun Wallet.hasPendingTransactions(): Boolean {
return recentTransactions.toPendingTransactions(address).isNotEmpty()
return getPendingTransactions().isNotEmpty()
}

View file

@ -97,7 +97,7 @@ class WalletMiddleware {
}
is WalletAction.CreateWallet -> {
if (walletState.isTangemTwins) {
store.dispatch(TwinCardsAction.CreateWallet.Create(
store.dispatch(TwinCardsAction.Wallet.Create(
walletState.twinCardsState.cardNumber!!,
CreateTwinWalletMode.CreateWallet
))