Updated on 2026-08-14

This commit is contained in:
Tangem 2024-05-20 13:44:49 +05:00
commit 9fe8c2d3fa
19 changed files with 202 additions and 136 deletions

View file

@ -41,7 +41,6 @@ import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.tokens.GetPolkadotCheckHasImmortalUseCase
import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.legacy.asLockable
import com.tangem.feature.qrscanning.QrScanningRouter
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
import com.tangem.features.managetokens.navigation.ManageTokensUi
@ -440,10 +439,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
}
private fun navigateToInitialScreen(intentWhichStartedActivity: Intent?) {
val canSaveWallets = runCatching { userWalletsListManager.asLockable()?.isLockedSync }
.fold(onSuccess = { true }, onFailure = { false })
if (canSaveWallets && userWalletsListManager.hasUserWallets) {
if (userWalletsListManager.isLockable && userWalletsListManager.hasUserWallets) {
store.dispatch(
NavigationAction.NavigateTo(
screen = AppScreen.Welcome,

View file

@ -12,6 +12,7 @@ import com.tangem.tap.domain.userWalletList.repository.UserWalletsKeysRepository
import com.tangem.tap.domain.userWalletList.repository.UserWalletsPublicInformationRepository
import com.tangem.tap.domain.userWalletList.repository.UserWalletsSensitiveInformationRepository
import com.tangem.tap.domain.userWalletList.utils.encryptionKey
import com.tangem.tap.domain.userWalletList.utils.lockAll
import com.tangem.tap.domain.userWalletList.utils.toUserWallets
import com.tangem.tap.domain.userWalletList.utils.updateWith
import kotlinx.coroutines.ExperimentalCoroutinesApi
@ -27,6 +28,8 @@ internal class BiometricUserWalletsListManager(
) : UserWalletsListManager.Lockable {
private val state = MutableStateFlow(State())
override val isLockable: Boolean = true
override val userWallets: Flow<List<UserWallet>>
get() = state
.mapLatest { it.userWallets }
@ -78,11 +81,13 @@ internal class BiometricUserWalletsListManager(
}
override fun lock() {
state.update { State() }
}
override fun isLockable(): Boolean {
return true
state.update { prevState ->
prevState.copy(
encryptionKeys = emptyList(),
userWallets = prevState.userWallets.lockAll(),
isLocked = true,
)
}
}
override suspend fun select(userWalletId: UserWalletId): CompletionResult<UserWallet> = catching {

View file

@ -33,31 +33,49 @@ internal class GeneralUserWalletsListManager(
) : UserWalletsListManager.Lockable {
private val applicationScope = CoroutineScope(dispatchers.io)
private val implementation = MutableStateFlow(runtimeUserWalletsListManager)
private val implementation: MutableStateFlow<UserWalletsListManager?> = MutableStateFlow(value = null)
private val requireImplementation: UserWalletsListManager
get() = requireNotNull(implementation.value) {
"UserWalletsListManager is not initialized"
}
init {
subscribeOnCurrentManager()
}
override val isLockable: Boolean
get() = requireImplementation.isLockable
override val userWallets: Flow<List<UserWallet>>
get() = implementation.flatMapLatest { it.userWallets }
get() = implementation.transformLatest { impl ->
if (impl != null && impl.hasUserWallets) {
emitAll(impl.userWallets)
}
}
override val selectedUserWallet: Flow<UserWallet>
get() = implementation.flatMapLatest { it.selectedUserWallet }
get() = implementation.transformLatest { impl ->
if (impl != null && impl.hasUserWallets) {
emitAll(impl.selectedUserWallet)
}
}
override val selectedUserWalletSync: UserWallet?
get() = implementation.value.selectedUserWalletSync
get() = requireImplementation.selectedUserWalletSync
override val hasUserWallets: Boolean
get() = implementation.value.hasUserWallets
get() = requireImplementation.hasUserWallets
override val walletsCount: Int
get() = implementation.value.walletsCount
get() = requireImplementation.walletsCount
override val isLocked: Flow<Boolean>
get() = implementation.flatMapLatest {
if (it is UserWalletsListManager.Lockable) {
it.isLocked
get() = implementation.transformLatest { impl ->
if (impl == null) return@transformLatest
if (impl is UserWalletsListManager.Lockable) {
emitAll(impl.isLocked)
} else {
error("RuntimeUserWalletsListManager is not lockable")
}
@ -65,43 +83,45 @@ internal class GeneralUserWalletsListManager(
override val isLockedSync: Boolean
get() {
val implementation = implementation.value
return if (implementation is UserWalletsListManager.Lockable) {
implementation.isLockedSync
val impl = requireImplementation
return if (impl is UserWalletsListManager.Lockable) {
impl.isLockedSync
} else {
error("RuntimeUserWalletsListManager is not lockable")
}
}
override suspend fun select(userWalletId: UserWalletId): CompletionResult<UserWallet> {
return implementation.value.select(userWalletId)
return requireImplementation.select(userWalletId)
}
override suspend fun save(userWallet: UserWallet, canOverride: Boolean): CompletionResult<Unit> {
return implementation.value.save(userWallet, canOverride)
return requireImplementation.save(userWallet, canOverride)
}
override suspend fun update(
userWalletId: UserWalletId,
update: suspend (UserWallet) -> UserWallet,
): CompletionResult<UserWallet> {
return implementation.value.update(userWalletId, update)
return requireImplementation.update(userWalletId, update)
}
override suspend fun delete(userWalletIds: List<UserWalletId>): CompletionResult<Unit> {
return implementation.value.delete(userWalletIds)
return requireImplementation.delete(userWalletIds)
}
override suspend fun clear(): CompletionResult<Unit> {
return implementation.value.clear()
return requireImplementation.clear()
}
override suspend fun get(userWalletId: UserWalletId): CompletionResult<UserWallet> {
return implementation.value.get(userWalletId)
return requireImplementation.get(userWalletId)
}
override suspend fun unlock(type: UserWalletsListManager.Lockable.UnlockType): CompletionResult<UserWallet> {
val implementation = implementation.value
val implementation = requireImplementation
return if (implementation is UserWalletsListManager.Lockable) {
implementation.unlock(type)
} else {
@ -110,7 +130,8 @@ internal class GeneralUserWalletsListManager(
}
override fun lock() {
val implementation = implementation.value
val implementation = requireImplementation
return if (implementation is UserWalletsListManager.Lockable) {
implementation.lock()
} else {
@ -118,10 +139,6 @@ internal class GeneralUserWalletsListManager(
}
}
override fun isLockable(): Boolean {
return implementation.value.isLockable()
}
private fun subscribeOnCurrentManager() {
appPreferencesStore.get(key = PreferencesKeys.SAVE_USER_WALLETS_KEY, default = false)
.distinctUntilChanged()
@ -144,17 +161,17 @@ internal class GeneralUserWalletsListManager(
destinationManager = possibleManager,
)
previousManager.clear()
previousManager?.clear()
}
.flowOn(dispatchers.io)
.launchIn(applicationScope)
}
private suspend fun copySelectedUserWallet(
sourceManager: UserWalletsListManager,
sourceManager: UserWalletsListManager?,
destinationManager: UserWalletsListManager,
): UserWalletsListManager {
sourceManager.selectedUserWalletSync?.let { selectedWallet ->
sourceManager?.selectedUserWalletSync?.let { selectedWallet ->
destinationManager.save(selectedWallet, canOverride = true)
}

View file

@ -13,6 +13,8 @@ import kotlinx.coroutines.flow.*
internal class RuntimeUserWalletsListManager : UserWalletsListManager {
private val state = MutableStateFlow(State())
override val isLockable: Boolean = false
override val userWallets: Flow<List<UserWallet>>
get() = state
.mapLatest { listOfNotNull(it.userWallet) }
@ -34,7 +36,7 @@ internal class RuntimeUserWalletsListManager : UserWalletsListManager {
* only 1 wallet stored in runtime implementation
*/
override val walletsCount: Int
get() = 1
get() = if (hasUserWallets) 1 else 0
override suspend fun select(userWalletId: UserWalletId): CompletionResult<UserWallet> = catching {
state.value.userWallet
@ -85,10 +87,6 @@ internal class RuntimeUserWalletsListManager : UserWalletsListManager {
state.value.userWallet ?: walletNotFound()
}
override fun isLockable(): Boolean {
return false
}
private fun saveInternal(userWallet: UserWallet): CompletionResult<Unit> = catching {
state.update { prevState ->
prevState.copy(

View file

@ -61,4 +61,14 @@ internal fun List<UserWallet>.updateWith(
?: wallet
}
}
}
}
internal fun List<UserWallet>.lockAll(): List<UserWallet> = map(UserWallet::lock)
internal fun UserWallet.lock(): UserWallet = copy(
scanResponse = scanResponse.copy(
card = scanResponse.card.copy(
wallets = emptyList(),
),
),
)

View file

@ -48,19 +48,30 @@ class WalletConnectSdkHelper {
}
@Suppress("MagicNumber")
suspend fun prepareTransactionData(data: EthTransactionData): WcTransactionData? {
suspend fun prepareTransactionData(data: EthTransactionData): WcTransactionData {
val transaction = data.transaction
val blockchain = Blockchain.fromNetworkId(data.networkId) ?: return null
val walletManager = getWalletManager(blockchain, data.rawDerivationPath) ?: return null
val blockchain = requireNotNull(Blockchain.fromNetworkId(data.networkId)) {
"Blockchain not found"
}
val walletManager = requireNotNull(getWalletManager(blockchain, data.rawDerivationPath)) {
"WalletManager not found"
}
walletManager.safeUpdate(isDemoCard())
val wallet = walletManager.wallet
val balance = wallet.amounts[AmountType.Coin]?.value ?: return null
val balance = requireNotNull(wallet.amounts[AmountType.Coin]?.value) {
"Coin balance not found"
}
val decimals = wallet.blockchain.decimals()
val value = (transaction.value ?: "0").hexToBigDecimal()
.movePointLeft(decimals) ?: return null
val value = (transaction.value ?: "0")
.hexToBigDecimal()
.movePointLeft(decimals)
requireNotNull(value) {
"Transaction amount is null"
}
val gasLimit = getGasLimitFromTx(value, walletManager, transaction)
@ -69,20 +80,23 @@ class WalletConnectSdkHelper {
is Result.Success -> result.data.toBigDecimal()
is Result.Failure -> {
(result.error as? Throwable)?.let { Timber.e(it, "getGasPrice failed") }
return null
error("Unable to get gas price: ${result.error}")
}
null -> return null
null -> error("Gas price is null")
}
val fee = (gasLimit * gasPrice).movePointLeft(decimals)
val total = value + fee
val destinationAddress = requireNotNull(transaction.to) { "Destination address is null" }
val transactionData = TransactionData(
amount = Amount(value, wallet.blockchain),
// TODO refactoring
fee = Fee.Common(Amount(fee, wallet.blockchain)),
sourceAddress = transaction.from,
destinationAddress = transaction.to!!,
destinationAddress = destinationAddress,
extras = EthereumTransactionExtras(
data = transaction.data.removePrefix(HEX_PREFIX).hexToBytes(),
gasLimit = gasLimit.toBigInteger(),
@ -102,6 +116,7 @@ class WalletConnectSdkHelper {
id = data.id,
type = data.type,
)
return WcTransactionData(
type = data.type,
transaction = transactionData,

View file

@ -374,6 +374,7 @@ internal class DefaultWalletConnectRepository(
override fun rejectRequest(requestData: RequestData, error: WalletConnectError) {
val session = currentSessions.find { it.topic == requestData.topic }
analyticsHandler.send(
WalletConnect.RequestHandled(
WalletConnect.RequestHandledParams(
@ -385,17 +386,18 @@ internal class DefaultWalletConnectRepository(
),
),
)
cancelRequest(requestData.topic, requestData.requestId)
cancelRequest(requestData.topic, requestData.requestId, error.error)
}
override fun cancelRequest(topic: String, id: Long) {
override fun cancelRequest(topic: String, id: Long, message: String) {
Web3Wallet.respondSessionRequest(
params = Wallet.Params.SessionRequestResponse(
sessionTopic = topic,
jsonRpcResponse = Wallet.Model.JsonRpcResponse.JsonRpcError(
id = id,
code = 0,
message = "",
message = message,
),
),
onSuccess = {},
@ -406,8 +408,8 @@ internal class DefaultWalletConnectRepository(
override fun reject() {
Web3Wallet.rejectSession(
params = Wallet.Params.SessionReject(
sessionProposal?.proposerPublicKey ?: "",
"",
proposerPublicKey = sessionProposal?.proposerPublicKey ?: "",
reason = "",
),
onSuccess = {
Timber.d("Rejected successfully: $it")

View file

@ -260,10 +260,18 @@ class WalletConnectInteractor(
)
else -> {
currentRequest = sessionRequest
val data = prepareRequestData(sessionRequest)
if (data != null) {
handler.onSessionRequest(data)
val data = prepareRequestData(sessionRequest).getOrElse { e ->
val wrappedError = e as? WalletConnectError ?: WalletConnectError.UnknownError(
message = e.localizedMessage ?: "Unknown error",
)
walletConnectRepository.rejectRequest(requestData, wrappedError)
handler.onSessionRejected(wrappedError)
return
}
handler.onSessionRequest(data)
}
}
}
@ -353,7 +361,9 @@ class WalletConnectInteractor(
}
}
private suspend fun prepareRequestData(sessionRequest: WalletConnectEvents.SessionRequest): WcPreparedRequest? {
private suspend fun prepareRequestData(
sessionRequest: WalletConnectEvents.SessionRequest,
): Result<WcPreparedRequest> {
return sessionRequestConverter.prepareRequest(sessionRequest, userWalletId)
}

View file

@ -27,5 +27,5 @@ interface WalletConnectRepository {
fun rejectRequest(requestData: RequestData, error: WalletConnectError)
fun cancelRequest(topic: String, id: Long)
fun cancelRequest(topic: String, id: Long, message: String = "")
}

View file

@ -4,6 +4,7 @@ import com.tangem.tap.domain.walletconnect.WalletConnectSdkHelper
import com.tangem.tap.domain.walletconnect2.domain.mapper.mapToTransaction
import com.tangem.tap.domain.walletconnect2.domain.models.BnbData
import com.tangem.tap.domain.walletconnect2.domain.models.EthTransactionData
import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectError
import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectEvents
import com.tangem.tap.features.details.redux.walletconnect.WcEthTransactionType
@ -17,15 +18,18 @@ internal class WcSessionRequestConverter(
suspend fun prepareRequest(
sessionRequest: WalletConnectEvents.SessionRequest,
userWalletId: String,
): WcPreparedRequest? {
val networkId = blockchainHelper.chainIdToNetworkIdOrNull(sessionRequest.chainId ?: "") ?: return null
): Result<WcPreparedRequest> = runCatching {
val networkId = requireNotNull(blockchainHelper.chainIdToNetworkIdOrNull(sessionRequest.chainId.orEmpty())) {
"Failed to get network ID for chain ID: ${sessionRequest.chainId}"
}
val derivationPath = getDerivationPath(
sessionsRepository = sessionsRepository,
sessionRequest = sessionRequest,
userWalletId = userWalletId,
walletAddress = getWalletAddress(sessionRequest.request),
)
return when (val request = sessionRequest.request) {
when (val request = sessionRequest.request) {
is WcRequest.EthSendTransaction -> {
val data = sdkHelper.prepareTransactionData(
EthTransactionData(
@ -38,7 +42,8 @@ internal class WcSessionRequestConverter(
metaName = sessionRequest.metaName,
metaUrl = sessionRequest.metaUrl,
),
) ?: return null
)
WcPreparedRequest.EthTransaction(
preparedRequestData = data,
topic = sessionRequest.topic,
@ -58,7 +63,8 @@ internal class WcSessionRequestConverter(
metaName = sessionRequest.metaName,
metaUrl = sessionRequest.metaUrl,
),
) ?: return null
)
WcPreparedRequest.EthTransaction(
preparedRequestData = data,
topic = sessionRequest.topic,
@ -122,7 +128,7 @@ internal class WcSessionRequestConverter(
derivationPath = derivationPath,
)
}
else -> null
else -> throw WalletConnectError.UnsupportedMethod
}
}

View file

@ -20,8 +20,12 @@ sealed class WalletConnectError(val error: String) : Exception() {
override val message: String?,
) : WalletConnectError("ExternalApprovalError")
object WrongUserWallet : WalletConnectError("WrongUserWallet")
object UnsupportedMethod : WalletConnectError("UnsupportedMethod")
object SigningError : WalletConnectError("SigningError")
object ValidationError : WalletConnectError("ValidationError")
data class UnknownError(
override val message: String,
) : WalletConnectError(message)
data object WrongUserWallet : WalletConnectError("WrongUserWallet")
data object UnsupportedMethod : WalletConnectError("UnsupportedMethod")
data object SigningError : WalletConnectError("SigningError")
data object ValidationError : WalletConnectError("ValidationError")
}

View file

@ -9,6 +9,7 @@ import com.tangem.domain.qrscanning.models.SourceType
import com.tangem.feature.qrscanning.QrScanningRouter
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
@ -20,6 +21,7 @@ import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.coroutines.launch
import org.rekotlin.Action
import org.rekotlin.Middleware
@ -135,6 +137,17 @@ class WalletConnectMiddleware {
),
)
}
is WalletConnectError.UnknownError -> {
store.dispatchOnMain(
GlobalAction.ShowDialog(
AppDialog.SimpleOkDialogRes(
headerId = R.string.wallet_connect_title,
messageId = R.string.wallet_connect_error_with_framework_message,
args = listOf(action.error.message),
),
),
)
}
is WalletConnectError.ExternalApprovalError -> {
Timber.e(action.error, "ExternalApprovalError ${action.error.message}")
// do not show dialog on this event

View file

@ -25,7 +25,7 @@
},
{
"name": "TOKEN_LIST_LCE_ENABLED",
"version": "5.10.0"
"version": "5.11.0"
},
{
"name": "CARDANO_TOKENS_SUPPORT_ENABLED",

View file

@ -7,6 +7,11 @@ import kotlinx.coroutines.flow.Flow
interface UserWalletsListManager {
/**
* Indicates that the [UserWalletsListManager] is [UserWalletsListManager.Lockable]
* */
val isLockable: Boolean
/** [Flow] with all saved [UserWallet]s updates */
val userWallets: Flow<List<UserWallet>>
@ -84,11 +89,6 @@ interface UserWalletsListManager {
*/
suspend fun get(userWalletId: UserWalletId): CompletionResult<UserWallet>
/**
* Indicates that the [UserWalletsListManager] supports [UserWalletsListManager.Lockable]
* */
fun isLockable(): Boolean
interface Lockable : UserWalletsListManager {
/**

View file

@ -49,7 +49,7 @@ suspend fun UserWalletsListManager.unlockIfLockable(type: UnlockType = UnlockTyp
* [UserWalletsListManager.Lockable] otherwise
* */
fun UserWalletsListManager.asLockable(): UserWalletsListManager.Lockable? {
if (this.isLockable()) {
if (this.isLockable) {
return this as? UserWalletsListManager.Lockable
}
return null

View file

@ -1,17 +1,14 @@
package com.tangem.domain.wallets.usecase
import arrow.core.Either
import arrow.core.left
import arrow.core.raise.either
import arrow.core.right
import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.DeleteWalletError
import com.tangem.domain.wallets.models.UserWalletId
/**
* Use case for updating user wallet
* Use case for deleting user wallet
*
* @property userWalletsListManager user wallets list manager
*
@ -19,13 +16,21 @@ import com.tangem.domain.wallets.models.UserWalletId
*/
class DeleteWalletUseCase(private val userWalletsListManager: UserWalletsListManager) {
suspend operator fun invoke(userWalletId: UserWalletId): Either<DeleteWalletError, Unit> {
/**
* Deletes user wallet with provided ID.
*
* @param userWalletId ID of user wallet to be deleted.
*
* @return [Either] with [DeleteWalletError] or [Boolean] which indicates that there are still saved wallets.
* */
suspend operator fun invoke(userWalletId: UserWalletId): Either<DeleteWalletError, Boolean> {
return either {
userWalletsListManager.delete(userWalletIds = listOf(userWalletId))
.doOnSuccess { return Unit.right() }
.doOnFailure { return DeleteWalletError.UnableToDelete.left() }
.doOnFailure {
raise(DeleteWalletError.UnableToDelete)
}
return Unit.right()
userWalletsListManager.hasUserWallets
}
}
}

View file

@ -5,7 +5,6 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.navigation.AppScreen
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.settings.CanUseBiometryUseCase
import com.tangem.domain.settings.IsWalletsScrollPreviewEnabled
@ -32,10 +31,11 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.saveIn
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.*
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
import javax.inject.Inject
@Suppress("LongParameterList")
@ -197,9 +197,9 @@ internal class WalletViewModel @Inject constructor(
is WalletsUpdateActionResolver.Action.UpdateWalletName -> {
stateHolder.update(transformer = RenameWalletTransformer(action.selectedWalletId, action.name))
}
is WalletsUpdateActionResolver.Action.NoAccessibleWallets -> closeScreen(screen = AppScreen.Welcome)
is WalletsUpdateActionResolver.Action.NoWallets -> closeScreen(screen = AppScreen.Home)
is WalletsUpdateActionResolver.Action.Unknown -> Unit
is WalletsUpdateActionResolver.Action.Unknown -> {
Timber.w("Unable to perfom action: $action")
}
}
}
@ -313,13 +313,6 @@ internal class WalletViewModel @Inject constructor(
)
}
private fun closeScreen(screen: AppScreen) {
if (!screenLifecycleProvider.isBackgroundState.value) {
stateHolder.clear()
router.popBackStack(screen = screen)
}
}
private fun scrollToWallet(index: Int, onConsume: () -> Unit = {}) {
stateHolder.update(
ScrollToWalletTransformer(

View file

@ -1,5 +1,6 @@
package com.tangem.feature.wallet.presentation.wallet.viewmodels
import arrow.core.getOrElse
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
@ -23,16 +24,14 @@ internal class WalletsUpdateActionResolver @Inject constructor(
) {
fun resolve(wallets: List<UserWallet>, currentState: WalletScreenState): Action {
val selectedWallet = wallets.getSelectedWallet()
val selectedWallet = getSelectedWalletSyncUseCase().getOrElse {
error("Unable to find selected wallet: $it")
}
val action = if (selectedWallet == null) {
createNoSelectedWalletAction(wallets)
val action = if (isFirstInitialization(currentState)) {
createInitializeWalletsAction(wallets, selectedWallet)
} else {
if (isFirstInitialization(currentState)) {
createInitializeWalletsAction(wallets, selectedWallet)
} else {
getUpdateContentAction(currentState, wallets, selectedWallet)
}
getUpdateContentAction(currentState, wallets, selectedWallet)
}
Timber.d("Resolved action: $action")
@ -40,22 +39,6 @@ internal class WalletsUpdateActionResolver @Inject constructor(
return action
}
private fun List<UserWallet>.getSelectedWallet(): UserWallet? {
return when {
isEmpty() -> null
size == 1 -> if (first().isLocked) null else first()
else -> getSelectedWalletSyncUseCase().fold(ifLeft = { null }, ifRight = { it })
}
}
private fun createNoSelectedWalletAction(wallets: List<UserWallet>): Action {
return when {
wallets.isEmpty() -> Action.NoWallets
wallets.all(UserWallet::isLocked) -> Action.NoAccessibleWallets
else -> Action.Unknown
}
}
private fun isFirstInitialization(state: WalletScreenState): Boolean {
return state.selectedWalletIndex == NOT_INITIALIZED_WALLET_INDEX
}
@ -289,10 +272,6 @@ internal class WalletsUpdateActionResolver @Inject constructor(
}
}
data object NoAccessibleWallets : Action()
data object NoWallets : Action()
data object Unknown : Action()
}
}

View file

@ -1,7 +1,11 @@
package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
import arrow.core.getOrElse
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.domain.card.DeleteSavedAccessCodesUseCase
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.core.navigation.ReduxNavController
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.DeleteWalletUseCase
@ -43,6 +47,7 @@ internal class WalletCardClickIntentsImplementor @Inject constructor(
private val deleteSavedAccessCodesUseCase: DeleteSavedAccessCodesUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
private val reduxStateHolder: ReduxStateHolder,
private val reduxNavController: ReduxNavController,
private val dispatchers: CoroutineDispatcherProvider,
) : BaseWalletClickIntents(), WalletCardClickIntents {
@ -81,18 +86,26 @@ internal class WalletCardClickIntentsImplementor @Inject constructor(
viewModelScope.launch(dispatchers.main) {
walletScreenContentLoader.cancel(userWalletId)
val deletedUserWallet = getUserWalletUseCase(userWalletId).getOrNull() ?: return@launch
val walletToDelete = getUserWalletUseCase(userWalletId).getOrNull() ?: return@launch
val hasUserWallets = deleteWalletUseCase(userWalletId).getOrElse {
Timber.e("Unable to delete user wallet: $it")
return@launch
}
deleteSavedAccessCodesUseCase(cardId = deletedUserWallet.cardId)
.onLeft { Timber.e(it.toString()) }
deleteSavedAccessCodesUseCase(cardId = walletToDelete.cardId).onLeft {
Timber.e("Unable to delete user wallet access code: $it")
}
deleteWalletUseCase(userWalletId)
.onRight {
getSelectedWalletSyncUseCase().getOrNull()?.let {
reduxStateHolder.onUserWalletSelected(it)
}
if (hasUserWallets) {
val selectedWallet = getSelectedWalletSyncUseCase().getOrElse {
error("Unable to find selected wallet: $it")
}
.onLeft { Timber.e(it.toString()) }
reduxStateHolder.onUserWalletSelected(selectedWallet)
} else {
stateHolder.clear()
reduxNavController.navigate(NavigationAction.PopBackTo(AppScreen.Home))
}
}
}
}