Updated on 2026-08-14

This commit is contained in:
Tangem 2023-10-24 18:56:11 +08:00
parent c2d6b31378
commit a16a27546c
19 changed files with 107 additions and 44 deletions

View file

@ -2,6 +2,7 @@ package com.tangem.tap.common
import android.app.Dialog
import android.content.Context
import com.tangem.core.navigation.StateDialog
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.common.redux.global.GlobalState
import com.tangem.tap.common.ui.SimpleAlertDialog
@ -53,7 +54,7 @@ class DialogManager : StoreSubscriber<GlobalState> {
is AppDialog.SimpleOkDialogRes -> SimpleOkDialog.create(state.dialog, context)
is AppDialog.SimpleOkErrorDialog -> SimpleOkDialog.create(state.dialog, context)
is AppDialog.SimpleOkWarningDialog -> SimpleOkDialog.create(state.dialog, context)
is AppDialog.ScanFailsDialog -> ScanFailsDialog.create(context)
is StateDialog.ScanFailsDialog -> ScanFailsDialog.create(context)
is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context)
is AppDialog.TestActionsDialog -> TestActionsBottomSheetDialog(state.dialog, context)
is OnboardingDialog.TwinningProcessNotCompleted -> TwinningProcessNotCompletedDialog.create(context)

View file

@ -1,10 +1,10 @@
package com.tangem.tap.common.extensions
import com.tangem.core.navigation.NavigationAction
import com.tangem.core.navigation.StateDialog
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.scope

View file

@ -1,6 +1,7 @@
package com.tangem.tap.common.redux
import com.tangem.common.extensions.VoidCallback
import com.tangem.core.navigation.StateDialog
import com.tangem.tap.common.TestAction
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.features.wallet.models.Currency
@ -8,8 +9,6 @@ import com.tangem.tap.features.wallet.models.Currency
/**
[REDACTED_AUTHOR]
*/
interface StateDialog
sealed class AppDialog : StateDialog {
data class SimpleOkDialog(val header: String, val message: String, val onOk: VoidCallback? = null) : AppDialog()
data class SimpleOkErrorDialog(val message: String, val onOk: VoidCallback? = null) : AppDialog()
@ -33,8 +32,6 @@ sealed class AppDialog : StateDialog {
val action: VoidCallback? = null,
)
object ScanFailsDialog : AppDialog()
data class AddressInfoDialog(
val currency: Currency,
val addressData: WalletDataModel.AddressData,

View file

@ -2,6 +2,7 @@ package com.tangem.tap.common.redux.global
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.CompletionResult
import com.tangem.core.navigation.StateDialog
import com.tangem.datasource.config.ConfigManager
import com.tangem.datasource.config.models.ChatConfig
import com.tangem.domain.apptheme.model.AppThemeMode
@ -11,7 +12,10 @@ import com.tangem.tap.common.analytics.topup.TopUpController
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.feedback.FeedbackData
import com.tangem.tap.common.feedback.FeedbackManager
import com.tangem.tap.common.redux.*
import com.tangem.tap.common.redux.DebugErrorAction
import com.tangem.tap.common.redux.ErrorAction
import com.tangem.tap.common.redux.NotificationAction
import com.tangem.tap.common.redux.ToastNotificationAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager

View file

@ -4,6 +4,7 @@ import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.guard
import com.tangem.core.analytics.Analytics
import com.tangem.core.navigation.StateDialog
import com.tangem.datasource.config.models.Config
import com.tangem.domain.common.LogConfig
import com.tangem.domain.models.scan.CardDTO
@ -14,7 +15,6 @@ import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.features.send.redux.SendAction
import com.tangem.tap.features.wallet.redux.WalletAction
@ -34,7 +34,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import org.rekotlin.Action
import org.rekotlin.DispatchFunction
import org.rekotlin.Middleware
import java.util.Locale
@ -45,14 +44,14 @@ object GlobalMiddleware {
private val globalMiddlewareHandler: Middleware<AppState> = { dispatch, appState ->
{ nextDispatch ->
{ action ->
handleAction(action, appState, dispatch)
handleAction(action, appState)
nextDispatch(action)
}
}
}
@Suppress("LongMethod", "ComplexMethod")
private fun handleAction(action: Action, appState: () -> AppState?, dispatch: DispatchFunction) {
private fun handleAction(action: Action, appState: () -> AppState?) {
when (action) {
is GlobalAction.ScanFailsCounter.ChooseBehavior -> {
when (action.result) {
@ -61,7 +60,7 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di
if (action.result.error is TangemSdkError.UserCancelled) {
store.dispatch(GlobalAction.ScanFailsCounter.Increment)
if (store.state.globalState.scanCardFailsCounter >= 2) {
store.dispatchDialogShow(AppDialog.ScanFailsDialog)
store.dispatchDialogShow(StateDialog.ScanFailsDialog)
}
} else {
store.dispatch(GlobalAction.ScanFailsCounter.Reset)

View file

@ -1,5 +1,6 @@
package com.tangem.tap.common.redux.global
import com.tangem.core.navigation.StateDialog
import com.tangem.datasource.config.ConfigManager
import com.tangem.domain.apptheme.model.AppThemeMode
import com.tangem.domain.models.scan.ScanResponse
@ -7,7 +8,6 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.tap.common.analytics.topup.TopUpController
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.feedback.FeedbackManager
import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.domain.TapWalletManager
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
import com.tangem.tap.features.onboarding.OnboardingManager

View file

@ -8,10 +8,7 @@ import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.tap.activityResultCaller
import com.tangem.tap.common.CustomTabsManager
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.openFragment
import com.tangem.tap.common.extensions.popBackTo
import com.tangem.tap.common.extensions.shareText
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.store
import org.rekotlin.Middleware
@ -60,6 +57,7 @@ val navigationMiddleware: Middleware<AppState> = { _, state ->
intent.data = action.url
navState?.activity?.get()?.startActivity(intent)
}
is NavigationAction.OpenDialog -> store.dispatchDialogShow(action.stateDialog)
is NavigationAction.OpenBiometricsSettings -> {
val settingsAction = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {

View file

@ -5,9 +5,9 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.derivation.DerivationStyle
import com.tangem.core.navigation.StateDialog
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.domain.walletconnect2.domain.WcEthereumSignMessage
import com.tangem.tap.domain.walletconnect2.domain.WcPreparedRequest
import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectEvents

View file

@ -1,7 +1,7 @@
package com.tangem.tap.features.onboarding
import com.tangem.common.extensions.VoidCallback
import com.tangem.tap.common.redux.StateDialog
import com.tangem.core.navigation.StateDialog
/**
[REDACTED_AUTHOR]

View file

@ -2,7 +2,7 @@ package com.tangem.tap.features.onboarding.products.wallet.redux
import android.graphics.Bitmap
import android.net.Uri
import com.tangem.tap.common.redux.StateDialog
import com.tangem.core.navigation.StateDialog
import org.rekotlin.StateType
/**

View file

@ -6,8 +6,8 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.core.TangemSdkError
import com.tangem.core.navigation.StateDialog
import com.tangem.tap.common.analytics.events.Token.Send.AddressEntered
import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.common.redux.ToastNotificationAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage

View file

@ -4,10 +4,10 @@ import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.extensions.isZero
import com.tangem.core.navigation.StateDialog
import com.tangem.tap.common.CurrencyConverter
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.entities.IndeterminateProgressButton
import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.common.text.DecimalDigitsInputFilter
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
@ -57,7 +57,7 @@ data class SendState(
return if (!this.coinIsConvertible()) value else coinConverter!!.toCrypto(value)
}
fun convertFiatToToken(value: BigDecimal): BigDecimal {
private fun convertFiatToToken(value: BigDecimal): BigDecimal {
return if (!this.tokenIsConvertible()) value else tokenConverter!!.toCrypto(value)
}

View file

@ -1,8 +1,8 @@
package com.tangem.tap.features.wallet.redux.models
import com.tangem.blockchain.common.Amount
import com.tangem.core.navigation.StateDialog
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.redux.StateDialog
import com.tangem.wallet.R
sealed interface WalletDialog : StateDialog {

View file

@ -23,6 +23,8 @@ sealed class NavigationAction : Action {
object OpenBiometricsSettings : NavigationAction()
data class OpenDialog(val stateDialog: StateDialog) : NavigationAction()
data class Share(val data: String) : NavigationAction()
data class ActivityCreated(val activity: WeakReference<AppCompatActivity>) : NavigationAction()

View file

@ -0,0 +1,6 @@
package com.tangem.core.navigation
interface StateDialog {
object ScanFailsDialog : StateDialog
}

View file

@ -18,6 +18,7 @@ import androidx.navigation.navArgument
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.core.navigation.ReduxNavController
import com.tangem.core.navigation.StateDialog
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.onboarding.navigation.OnboardingRouter
@ -136,6 +137,10 @@ internal class DefaultWalletRouter(private val reduxNavController: ReduxNavContr
reduxNavController.navigate(action = NavigationAction.NavigateTo(AppScreen.AddTokens))
}
override fun openScanFailedDialog() {
reduxNavController.navigate(action = NavigationAction.OpenDialog(StateDialog.ScanFailsDialog))
}
private companion object {
const val BACKSTACK_ENTRY_COUNT_TO_CLOSE_WALLET_SCREEN = 2
}

View file

@ -56,4 +56,6 @@ internal interface InnerWalletRouter : WalletRouter {
/** Open manage tokens screen */
fun openManageTokensScreen()
fun openScanFailedDialog()
}

View file

@ -0,0 +1,51 @@
package com.tangem.feature.wallet.presentation.wallet.domain
import arrow.core.Either
import arrow.core.raise.either
import com.tangem.common.core.TangemSdkError
import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.userwallets.UserWalletBuilder
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
import javax.inject.Inject
internal class ScanCardToUnlockWalletClickHandler @Inject constructor(
private val scanCardProcessor: ScanCardProcessor,
private val saveWalletUseCase: SaveWalletUseCase,
) {
private var scanFailsCounter = 0
suspend operator fun invoke(walletId: UserWalletId): Either<ScanCardToUnlockWalletError, Unit> {
return either {
scanCardProcessor.scan()
.doOnSuccess { scanResponse ->
scanFailsCounter = 0
// If card's public key is null then user wallet will be null
val scannedWallet = UserWalletBuilder(scanResponse = scanResponse).build()
if (walletId == scannedWallet?.walletId) {
saveWalletUseCase(userWallet = scannedWallet, canOverride = true)
} else {
raise(ScanCardToUnlockWalletError.WrongCardIsScanned)
}
}
.doOnFailure {
if (it is TangemSdkError.UserCancelled) {
scanFailsCounter++
if (scanFailsCounter >= 2) raise(ScanCardToUnlockWalletError.ManyScanFails)
}
}
}
}
}
internal sealed class ScanCardToUnlockWalletError {
object WrongCardIsScanned : ScanCardToUnlockWalletError()
object ManyScanFails : ScanCardToUnlockWalletError()
}

View file

@ -10,7 +10,6 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.address.AddressType
import com.tangem.common.Provider
import com.tangem.common.card.EllipticCurve
import com.tangem.common.doOnSuccess
import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.extensions.isZero
import com.tangem.common.extensions.toMapKey
@ -30,7 +29,6 @@ import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.balancehiding.IsBalanceHiddenUseCase
import com.tangem.domain.balancehiding.ListenToFlipsUseCase
import com.tangem.domain.card.DerivePublicKeysUseCase
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.card.SetCardWasScannedUseCase
import com.tangem.domain.card.WasCardScannedUseCase
import com.tangem.domain.common.CardTypesResolver
@ -54,7 +52,6 @@ import com.tangem.domain.tokens.models.analytics.TokenScreenAnalyticsEvent
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.domain.userwallets.UserWalletBuilder
import com.tangem.domain.walletconnect.WalletConnectActions
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UnlockWalletsError
@ -65,6 +62,8 @@ import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
import com.tangem.feature.wallet.presentation.wallet.analytics.PortfolioEvent
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
import com.tangem.feature.wallet.presentation.wallet.domain.ScanCardToUnlockWalletClickHandler
import com.tangem.feature.wallet.presentation.wallet.domain.ScanCardToUnlockWalletError
import com.tangem.feature.wallet.presentation.wallet.state.*
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState
import com.tangem.feature.wallet.presentation.wallet.state.factory.TokenListWithWallet
@ -93,7 +92,6 @@ import kotlin.properties.Delegates
internal class WalletViewModel @Inject constructor(
// region Parameters
private val getWalletsUseCase: GetWalletsUseCase,
private val saveWalletUseCase: SaveWalletUseCase,
getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
private val selectWalletUseCase: SelectWalletUseCase,
private val updateWalletUseCase: UpdateWalletUseCase,
@ -105,7 +103,6 @@ internal class WalletViewModel @Inject constructor(
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase,
private val scanCardProcessor: ScanCardProcessor,
private val derivePublicKeysUseCase: DerivePublicKeysUseCase,
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase,
@ -131,6 +128,7 @@ internal class WalletViewModel @Inject constructor(
private val setWalletWithFundsFoundUseCase: SetWalletWithFundsFoundUseCase,
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
private val isDemoCardUseCase: IsDemoCardUseCase,
private val scanCardToUnlockWalletUseCase: ScanCardToUnlockWalletClickHandler,
wasCardScannedUseCase: WasCardScannedUseCase,
isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
isNeedToBackupUseCase: IsNeedToBackupUseCase,
@ -414,22 +412,22 @@ internal class WalletViewModel @Inject constructor(
val lockedWallet = getWallet(index = state.walletsListConfig.selectedWalletIndex)
viewModelScope.launch(dispatchers.io) {
scanCardProcessor.scan()
.doOnSuccess { scanResponse ->
// If card's public key is null then user wallet will be null
val scannedWallet = UserWalletBuilder(scanResponse = scanResponse).build()
viewModelScope.launch(dispatchers.main) {
scanCardToUnlockWalletUseCase(walletId = lockedWallet.walletId)
.onLeft { error ->
when (error) {
ScanCardToUnlockWalletError.WrongCardIsScanned -> {
delay(timeMillis = DELAY_SDK_DIALOG_CLOSE)
if (lockedWallet.walletId == scannedWallet?.walletId) {
saveWalletUseCase(userWallet = scannedWallet, canOverride = true)
} else {
delay(timeMillis = DELAY_SDK_DIALOG_CLOSE)
uiState = stateFactory.getStateAndTriggerEvent(
state = uiState,
event = WalletEvent.ShowAlert(state = WalletAlertState.WrongCardIsScanned),
setUiState = { uiState = it },
)
uiState = stateFactory.getStateAndTriggerEvent(
state = uiState,
event = WalletEvent.ShowAlert(state = WalletAlertState.WrongCardIsScanned),
setUiState = { uiState = it },
)
}
ScanCardToUnlockWalletError.ManyScanFails -> {
router.openScanFailedDialog()
}
}
}
}