Updated on 2026-08-14

This commit is contained in:
Tangem 2024-03-14 13:01:17 +00:00
commit 5a83aace89
19 changed files with 152 additions and 131 deletions

View file

@ -6,13 +6,14 @@ import androidx.annotation.StringRes
import com.tangem.Message
import com.tangem.TangemSdk
import com.tangem.common.*
import com.tangem.common.authentication.KeystoreManager
import com.tangem.common.authentication.keystore.KeystoreManager
import com.tangem.common.card.FirmwareVersion
import com.tangem.common.core.*
import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.services.secure.SecureStorage
import com.tangem.common.usersCode.UserCodeRepository
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.models.Basic
import com.tangem.crypto.bip39.DefaultMnemonic
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.card.repository.CardSdkConfigRepository
@ -25,7 +26,6 @@ import com.tangem.operations.derivation.DerivationTaskResponse
import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
import com.tangem.operations.pins.SetUserCodeCommand
import com.tangem.operations.usersetttings.SetUserCodeRecoveryAllowedTask
import com.tangem.core.analytics.models.Basic
import com.tangem.tap.derivationsFinder
import com.tangem.tap.domain.tasks.product.CreateProductWalletTask
import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse

View file

@ -3,7 +3,7 @@ package com.tangem.tap.domain.userWalletList.di
import android.content.Context
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import com.tangem.common.authentication.AuthenticatedStorage
import com.tangem.common.authentication.storage.AuthenticatedStorage
import com.tangem.common.json.TangemSdkAdapter
import com.tangem.common.services.secure.SecureStorage
import com.tangem.datasource.local.preferences.AppPreferencesStore

View file

@ -3,7 +3,7 @@ package com.tangem.tap.domain.userWalletList.di
import android.content.Context
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import com.tangem.common.authentication.AuthenticatedStorage
import com.tangem.common.authentication.storage.AuthenticatedStorage
import com.tangem.common.json.TangemSdkAdapter
import com.tangem.common.services.secure.SecureStorage
import com.tangem.domain.wallets.legacy.UserWalletsListManager

View file

@ -131,16 +131,25 @@ internal class GeneralUserWalletsListManager(
Timber.d("Switch to ${manager::class.java.simpleName}")
implementation.value = manager
clearOldManager(manager)
}
.flowOn(dispatchers.io)
.launchIn(applicationScope)
}
/** Copy data from [old] manager and clean it */
/** Copy data from [old] manager */
private suspend fun UserWalletsListManager.copyFrom(old: UserWalletsListManager): UserWalletsListManager {
old.selectedUserWalletSync?.let { this.save(it) }
old.clear()
return this
}
private suspend fun clearOldManager(current: UserWalletsListManager) {
if (current == biometricUserWalletsListManager) {
runtimeUserWalletsListManager.clear()
} else {
biometricUserWalletsListManager.clear()
}
}
}

View file

@ -1,6 +1,6 @@
package com.tangem.tap.domain.userWalletList.repository
import com.tangem.common.authentication.KeystoreManager
import com.tangem.common.authentication.keystore.KeystoreManager
import com.tangem.utils.Provider
import javax.crypto.SecretKey
@ -8,15 +8,18 @@ internal class DelegatedKeystoreManager(
private val keystoreManagerProvider: Provider<KeystoreManager>,
) : KeystoreManager {
override suspend fun get(keyAlias: String): SecretKey? {
return keystoreManagerProvider().get(keyAlias)
override suspend fun get(masterKeyConfig: KeystoreManager.MasterKeyConfig, keyAlias: String): SecretKey? {
return keystoreManagerProvider().get(masterKeyConfig, keyAlias)
}
override suspend fun get(keyAliases: Collection<String>): Map<String, SecretKey> {
return keystoreManagerProvider().get(keyAliases)
override suspend fun get(
masterKeyConfig: KeystoreManager.MasterKeyConfig,
keyAliases: Collection<String>,
): Map<String, SecretKey> {
return keystoreManagerProvider().get(masterKeyConfig, keyAliases)
}
override suspend fun store(keyAlias: String, key: SecretKey) {
return keystoreManagerProvider().store(keyAlias, key)
override suspend fun store(masterKeyConfig: KeystoreManager.MasterKeyConfig, keyAlias: String, key: SecretKey) {
keystoreManagerProvider().store(masterKeyConfig, keyAlias, key)
}
}

View file

@ -4,7 +4,7 @@ import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import com.tangem.common.*
import com.tangem.common.authentication.AuthenticatedStorage
import com.tangem.common.authentication.storage.AuthenticatedStorage
import com.tangem.common.core.TangemSdkError
import com.tangem.common.services.secure.SecureStorage
import com.tangem.domain.wallets.legacy.UserWalletsListError

View file

@ -96,5 +96,6 @@ internal sealed interface TokensListStateHolder {
override val tokens: Flow<PagingData<TokenItemState>>,
override val onTokensLoadStateChanged: (LoadState) -> Unit,
val onSaveButtonClick: () -> Unit,
val isSavingInProgress: Boolean,
) : TokensListStateHolder
}

View file

@ -29,7 +29,10 @@ import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.TextUnitType
import androidx.compose.ui.unit.dp
import androidx.paging.PagingData
import androidx.paging.compose.*
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemContentType
import androidx.paging.compose.itemKey
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.res.TangemTheme
import com.tangem.tap.features.tokens.impl.presentation.states.TokenItemState
@ -63,10 +66,11 @@ internal fun TokensListScreen(stateHolder: TokensListStateHolder, modifier: Modi
val verticalPadding = TangemTheme.dimens.spacing32
SaveChangesButton(
onClick = stateHolder.onSaveButtonClick,
modifier = Modifier.onSizeChanged {
with(density) { floatingButtonHeight = it.height.toDp() + verticalPadding }
},
showProgress = stateHolder.isSavingInProgress,
onClick = stateHolder.onSaveButtonClick,
)
}
},
@ -176,13 +180,14 @@ private fun DifferentAddressesWarning() {
}
@Composable
private fun SaveChangesButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
private fun SaveChangesButton(showProgress: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
PrimaryButton(
modifier = modifier
.imePadding()
.padding(horizontal = TangemTheme.dimens.spacing16)
.fillMaxWidth(),
text = stringResource(id = R.string.common_save_changes),
showProgress = showProgress,
onClick = onClick,
)
}
@ -237,6 +242,7 @@ private class TokensListScreenProvider : CollectionPreviewParameterProvider<Toke
),
onSaveButtonClick = {},
onTokensLoadStateChanged = {},
isSavingInProgress = false,
),
TokensListStateHolder.ReadContent(
toolbarState = TokensListToolbarState.Title.Read(

View file

@ -3,6 +3,7 @@ package com.tangem.tap.features.tokens.impl.presentation.viewmodels
import arrow.core.Either
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.data.tokens.utils.CryptoCurrencyFactory
import com.tangem.domain.card.DerivePublicKeysUseCase
@ -15,7 +16,7 @@ import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store
@ -125,7 +126,7 @@ internal class TokensListMigration(
val isNothingToDoWithBlockchain = blockchainsToAdd.isEmpty() && blockchainsToRemove.isEmpty()
if (isNothingToDoWithTokens && isNothingToDoWithBlockchain) {
store.dispatchDebugErrorNotification(message = "Nothing to save")
store.dispatchOnMain(NavigationAction.PopBackTo())
store.dispatchWithMain(NavigationAction.PopBackTo(screen = AppScreen.Wallet))
return
}
@ -134,9 +135,8 @@ internal class TokensListMigration(
derivePublicKeysUseCase(userWalletId = currentUserWallet.walletId, currencies = currencyList)
.onRight {
addCryptoCurrenciesUseCase(userWalletId = currentUserWallet.walletId, currencies = currencyList)
store.dispatchOnMain(NavigationAction.PopBackTo())
}
.onLeft { Timber.e("Failed to derive public keys: $it") }
.onLeft { Timber.e(it, "Failed to derive public keys") }
}
private suspend fun removeCurrenciesIfNeeded(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {

View file

@ -11,6 +11,8 @@ import androidx.lifecycle.viewModelScope
import androidx.paging.*
import com.tangem.blockchain.common.Blockchain
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.core.ui.extensions.getActiveIconRes
import com.tangem.core.ui.extensions.getGreyedOutIconRes
import com.tangem.domain.card.DerivePublicKeysUseCase
@ -24,6 +26,7 @@ import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
import com.tangem.domain.tokens.GetCryptoCurrenciesUseCase
import com.tangem.domain.tokens.TokenWithBlockchain
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.extensions.fullNameWithoutTestnet
import com.tangem.tap.common.extensions.getNetworkName
import com.tangem.tap.features.tokens.impl.domain.TokensListInteractor
@ -122,6 +125,7 @@ internal class TokensListViewModel @Inject constructor(
isDifferentAddressesBlockVisible = isDifferentAddressesBlockVisible(),
tokens = getInitialTokensList(),
onTokensLoadStateChanged = actionsHandler::onTokensLoadStateChanged,
isSavingInProgress = false,
onSaveButtonClick = actionsHandler::onSaveButtonClick,
)
} else {
@ -308,13 +312,20 @@ internal class TokensListViewModel @Inject constructor(
}
fun onSaveButtonClick() {
val state = uiState as? TokensListStateHolder.ManageContent ?: return
analyticsSender.sendWhenSaveButtonClicked()
viewModelScope.launch(dispatchers.main) {
uiState = state.copy(isSavingInProgress = true)
tokensListMigration.onSaveButtonClick(
changedTokensList = changedTokensList,
changedBlockchainList = changedBlockchainList,
)
uiState = state.copy(isSavingInProgress = false)
store.dispatchWithMain(NavigationAction.PopBackTo(screen = AppScreen.Wallet))
}
}

View file

@ -8,6 +8,8 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import timber.log.Timber
@Deprecated("Use shared preferences data store instead")
@ -16,6 +18,7 @@ internal class FileDataStore<Value : Any>(
private val adapter: JsonAdapter<Value>,
) : StringKeyDataStore<Value> {
private val mutex = Mutex()
private val writeTrigger = Trigger()
override suspend fun isEmpty(): Boolean {
val e = NotImplementedError("`isEmpty()` function not implemented for `FileDataStore`")
@ -28,7 +31,11 @@ internal class FileDataStore<Value : Any>(
override fun get(key: String): Flow<Value> {
return writeTrigger
.map { getInternal(key) }
.map {
mutex.withLock {
getInternal(key)
}
}
.filterNotNull()
.distinctUntilChanged()
}
@ -53,10 +60,12 @@ internal class FileDataStore<Value : Any>(
override suspend fun store(key: String, value: Value) {
try {
val json = adapter.toJson(value)
mutex.withLock {
val json = adapter.toJson(value)
fileReader.rewriteFile(json, key)
writeTrigger.trigger()
fileReader.rewriteFile(json, key)
writeTrigger.trigger()
}
} catch (e: Throwable) {
Timber.e(e, "Unable to write file: $key")
}

View file

@ -17,7 +17,7 @@
},
{
"name": "GENERAL_USER_WALLETS_LIST_MANAGER_ENABLED",
"version": "5.7.0"
"version": "5.8.0"
},
{
"name": "LOCAL_USER_LOGS_ENABLED",

View file

@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
@ -86,7 +87,8 @@ private fun Button(
Row(
modifier = modifier
.heightIn(min = TangemTheme.dimens.size36)
.background(color = backgroundColor, shape = shape)
.clip(shape)
.background(color = backgroundColor)
.clickable(enabled = config.enabled, onClick = config.onClick)
.padding(start = TangemTheme.dimens.spacing16, end = TangemTheme.dimens.spacing24)
.padding(vertical = TangemTheme.dimens.spacing8),

View file

@ -3,15 +3,21 @@ package com.tangem.data.common.cache
import com.tangem.datasource.local.cache.CacheKeysStore
import com.tangem.datasource.local.cache.model.CacheKey
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import org.joda.time.Duration
import org.joda.time.LocalDateTime
import timber.log.Timber
import java.util.concurrent.ConcurrentHashMap
internal class DefaultCacheRegistry(
private val cacheKeysStore: CacheKeysStore,
) : CacheRegistry {
private val mutex = Mutex()
private val mutexes = ConcurrentHashMap<String, Mutex>()
override suspend fun isExpired(key: String): Boolean {
val cacheKey = cacheKeysStore.getSyncOrNull(key) ?: return true
@ -41,27 +47,36 @@ internal class DefaultCacheRegistry(
expireIn: Duration,
block: suspend () -> Unit,
) {
val isExpired = isExpired(key) || skipCache
if (!isExpired) return
// use a separate mutexForKey for each key to avoid multiple calls block() to the same key
// also used mutex to safe create mutexForKey, otherwise it can lead to multiple calls for the same key
val mutexForKey = mutex.withLock {
mutexes.getOrPut(key) { Mutex() }
}
mutexForKey.withLock {
val isExpired = isExpired(key) || skipCache
if (!isExpired) {
return
}
try {
Timber.d("Invoke the action associated with the cache key: $key")
try {
Timber.d("Invoke the action associated with the cache key: $key")
cacheKeysStore.store(
key = CacheKey(
id = key,
updatedAt = LocalDateTime.now(),
expiresIn = expireIn,
),
)
cacheKeysStore.store(
key = CacheKey(
id = key,
updatedAt = LocalDateTime.now(),
expiresIn = expireIn,
),
)
block()
} catch (e: Throwable) {
Timber.e(e, "The action related to the cache key has failed: $key")
block()
} catch (e: Throwable) {
Timber.e(e, "The action related to the cache key has failed: $key")
invalidate(key)
invalidate(key)
throw e
throw e
}
}
}
}

View file

@ -56,8 +56,9 @@ internal class DefaultNetworksRepository(
.cancellable()
override suspend fun fetchNetworkPendingTransactions(userWalletId: UserWalletId, networks: Set<Network>) {
val currencies = getCurrencies(userWalletId, networks)
withContext(dispatchers.io) {
fetchNetworksPendingTransactions(userWalletId, networks)
fetchNetworksPendingTransactions(userWalletId, networks, currencies)
}
}
@ -80,23 +81,28 @@ internal class DefaultNetworksRepository(
networks: Set<Network>,
refresh: Boolean,
) {
val currencies = getCurrencies(userWalletId, networks)
coroutineScope {
networks
.map { network ->
async {
fetchNetworkStatusIfCacheExpired(userWalletId, network, refresh)
fetchNetworkStatusIfCacheExpired(userWalletId, network, currencies, refresh)
}
}
.awaitAll()
}
}
private suspend fun fetchNetworksPendingTransactions(userWalletId: UserWalletId, networks: Set<Network>) {
private suspend fun fetchNetworksPendingTransactions(
userWalletId: UserWalletId,
networks: Set<Network>,
currencies: Sequence<CryptoCurrency>,
) {
coroutineScope {
networks
.map { network ->
async {
fetchNetworkPendingTransactions(userWalletId, network)
fetchNetworkPendingTransactions(userWalletId, network, currencies)
}
}
.awaitAll()
@ -106,22 +112,28 @@ internal class DefaultNetworksRepository(
private suspend fun fetchNetworkStatusIfCacheExpired(
userWalletId: UserWalletId,
network: Network,
currencies: Sequence<CryptoCurrency>,
refresh: Boolean,
) {
cacheRegistry.invokeOnExpire(
key = getNetworksStatusesCacheKey(userWalletId, network),
skipCache = refresh,
block = { fetchNetworkStatus(userWalletId, network) },
block = { fetchNetworkStatus(userWalletId, network, currencies) },
)
}
private suspend fun fetchNetworkStatus(userWalletId: UserWalletId, network: Network) {
val currencies = getCurrencies(userWalletId, network)
private suspend fun fetchNetworkStatus(
userWalletId: UserWalletId,
network: Network,
currencies: Sequence<CryptoCurrency>,
) {
val result = walletManagersFacade.update(
userWalletId = userWalletId,
network = network,
extraTokens = currencies.filterIsInstance<CryptoCurrency.Token>().toSet(),
extraTokens = currencies
.filterIsInstance<CryptoCurrency.Token>()
.filter { it.network == network }
.toSet(),
)
withContext(NonCancellable) {
@ -137,9 +149,11 @@ internal class DefaultNetworksRepository(
networksStatusesStore.store(userWalletId, networkStatus)
}
private suspend fun fetchNetworkPendingTransactions(userWalletId: UserWalletId, network: Network) {
val currencies = getCurrencies(userWalletId, network)
private suspend fun fetchNetworkPendingTransactions(
userWalletId: UserWalletId,
network: Network,
currencies: Sequence<CryptoCurrency>,
) {
val result = walletManagersFacade.updatePendingTransactions(
userWalletId = userWalletId,
network = network,
@ -158,7 +172,7 @@ internal class DefaultNetworksRepository(
networksStatusesStore.store(userWalletId, networkStatus)
}
private suspend fun getCurrencies(userWalletId: UserWalletId, network: Network): Sequence<CryptoCurrency> {
private suspend fun getCurrencies(userWalletId: UserWalletId, networks: Set<Network>): Sequence<CryptoCurrency> {
val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
"Unable to find user wallet with provided ID: $userWalletId"
}
@ -180,7 +194,7 @@ internal class DefaultNetworksRepository(
}
}
return currencies.filter { it.network == network }
return currencies.filter { networks.contains(it.network) }
}
private suspend fun invalidateCacheKeyIfNeeded(

View file

@ -17,6 +17,8 @@ import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
internal class DefaultQuotesRepository(
@ -32,6 +34,7 @@ internal class DefaultQuotesRepository(
@Volatile
private var quotesFetchedForAppCurrency: String? = null
private val mutex = Mutex()
@OptIn(ExperimentalCoroutinesApi::class)
override fun getQuotesUpdates(currenciesIds: Set<CryptoCurrency.ID>): Flow<Set<Quote>> {
@ -42,7 +45,6 @@ internal class DefaultQuotesRepository(
.filterNotNull()
.flatMapLatest { appCurrency ->
fetchExpiredQuotes(currenciesIds, appCurrency.id, refresh = false)
quotesStore.get(currenciesIds).map(quotesConverter::convertSet)
}
.cancellable()
@ -79,15 +81,19 @@ internal class DefaultQuotesRepository(
appCurrencyId: String,
refresh: Boolean,
) {
val expiredCurrenciesIds = filterExpiredCurrenciesIds(
currenciesIds = currenciesIds,
refresh = refresh || quotesFetchedForAppCurrency != appCurrencyId,
)
if (expiredCurrenciesIds.isEmpty()) return
// TODO("[REDACTED_JIRA]") need refactor working with quotesFetchedForAppCurrency,
// it changes after filterExpiredCurrenciesIds
// calls with different coroutines and lead to fetchQuotes
mutex.withLock {
val expiredCurrenciesIds = filterExpiredCurrenciesIds(
currenciesIds = currenciesIds,
refresh = refresh || quotesFetchedForAppCurrency != appCurrencyId,
)
if (expiredCurrenciesIds.isEmpty()) return
quotesFetchedForAppCurrency = appCurrencyId
fetchQuotes(expiredCurrenciesIds, appCurrencyId)
quotesFetchedForAppCurrency = appCurrencyId
fetchQuotes(expiredCurrenciesIds, appCurrencyId)
}
}
private suspend fun fetchQuotes(rawCurrenciesIds: Set<String>, appCurrencyId: String) {
@ -118,7 +124,6 @@ internal class DefaultQuotesRepository(
): Set<String> {
return currenciesIds.fold(hashSetOf()) { acc, currencyId ->
val rawCurrencyId = currencyId.rawCurrencyId
if (rawCurrencyId != null && rawCurrencyId !in acc) {
cacheRegistry.invokeOnExpire(
key = getQuoteCacheKey(rawCurrencyId),

View file

@ -14,7 +14,6 @@ import com.tangem.domain.settings.ShouldShowSaveWalletScreenUseCase
import com.tangem.domain.walletconnect.WalletConnectActions
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.feature.wallet.presentation.deeplink.WalletDeepLinksHandler
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
@ -53,7 +52,6 @@ internal class WalletViewModel @Inject constructor(
private val getWalletsUseCase: GetWalletsUseCase,
private val shouldShowSaveWalletScreenUseCase: ShouldShowSaveWalletScreenUseCase,
private val canUseBiometryUseCase: CanUseBiometryUseCase,
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val isWalletsScrollPreviewEnabled: IsWalletsScrollPreviewEnabled,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
analyticsEventsHandler: AnalyticsEventHandler,
@ -74,7 +72,7 @@ internal class WalletViewModel @Inject constructor(
suggestToEnableBiometrics()
subscribeOnWalletsUpdateFlow()
subscribeToUserWalletsUpdates()
subscribeOnBalanceHiding()
subscribeOnSelectedWalletFlow()
}
@ -112,25 +110,12 @@ internal class WalletViewModel @Inject constructor(
return router.isWalletLastScreen() && shouldShowSaveWalletScreenUseCase() && canUseBiometryUseCase()
}
private fun subscribeOnWalletsUpdateFlow() {
viewModelScope.launch(dispatchers.main) {
shouldSaveUserWalletsUseCase()
.conflate()
.distinctUntilChanged()
.collectLatest(::subscribeToUserWalletsUpdates)
}
}
private fun subscribeToUserWalletsUpdates(shouldSaveUserWallet: Boolean) {
private fun subscribeToUserWalletsUpdates() {
getWalletsUseCase()
.conflate()
.distinctUntilChanged()
.map {
walletsUpdateActionResolver.resolve(
wallets = it,
currentState = stateHolder.value,
canSaveWallets = shouldSaveUserWallet,
)
walletsUpdateActionResolver.resolve(wallets = it, currentState = stateHolder.value)
}
.onEach(::updateWallets)
.flowOn(dispatchers.main)
@ -182,14 +167,6 @@ internal class WalletViewModel @Inject constructor(
private suspend fun updateWallets(action: WalletsUpdateActionResolver.Action) {
when (action) {
is WalletsUpdateActionResolver.Action.InitializeWallets -> initializeWallets(action)
is WalletsUpdateActionResolver.Action.ReinitializeWallets -> {
walletScreenContentLoader.load(
userWallet = action.selectedWallet,
clickIntents = clickIntents,
isRefresh = true,
coroutineScope = viewModelScope,
)
}
is WalletsUpdateActionResolver.Action.ReinitializeWallet -> reinitializeWallet(action)
is WalletsUpdateActionResolver.Action.AddWallet -> addWallet(action)
is WalletsUpdateActionResolver.Action.DeleteWallet -> deleteWallet(action)

View file

@ -22,24 +22,16 @@ internal class WalletsUpdateActionResolver @Inject constructor(
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
) {
private var isInitialized: Boolean = false
private var canSaveWallets: Boolean = false
fun resolve(wallets: List<UserWallet>, currentState: WalletScreenState, canSaveWallets: Boolean): Action {
fun resolve(wallets: List<UserWallet>, currentState: WalletScreenState): Action {
val selectedWallet = wallets.getSelectedWallet()
val action = if (selectedWallet == null) {
createNoSelectedWalletAction(wallets)
} else {
when {
isFirstInitialization(currentState) -> {
createInitializeWalletsAction(wallets, selectedWallet, canSaveWallets)
}
isReinitialization(canSaveWallets) -> {
this.canSaveWallets = canSaveWallets
Action.ReinitializeWallets(selectedWallet = selectedWallet)
}
else -> getUpdateContentAction(currentState, wallets, selectedWallet)
if (isFirstInitialization(currentState)) {
createInitializeWalletsAction(wallets, selectedWallet)
} else {
getUpdateContentAction(currentState, wallets, selectedWallet)
}
}
@ -68,14 +60,7 @@ internal class WalletsUpdateActionResolver @Inject constructor(
return state.selectedWalletIndex == NOT_INITIALIZED_WALLET_INDEX
}
private fun createInitializeWalletsAction(
wallets: List<UserWallet>,
selectedWallet: UserWallet,
canSaveWallets: Boolean,
): Action {
this.isInitialized = true
this.canSaveWallets = canSaveWallets
private fun createInitializeWalletsAction(wallets: List<UserWallet>, selectedWallet: UserWallet): Action {
return Action.InitializeWallets(
selectedWalletIndex = wallets.indexOfWallet(selectedWallet.walletId),
selectedWallet = selectedWallet,
@ -83,10 +68,6 @@ internal class WalletsUpdateActionResolver @Inject constructor(
)
}
private fun isReinitialization(canSaveWallets: Boolean): Boolean {
return isInitialized && this.canSaveWallets != canSaveWallets
}
private fun getUpdateContentAction(
state: WalletScreenState,
wallets: List<UserWallet>,
@ -232,18 +213,6 @@ internal class WalletsUpdateActionResolver @Inject constructor(
}
}
/**
* Reinitialize wallets. Example, if user turned on wallets saving
*
* @property selectedWallet selected wallet
*/
data class ReinitializeWallets(val selectedWallet: UserWallet) : Action() {
override fun toString(): String {
return "ReinitializeWallets(selectedWallet = ${selectedWallet.walletId})"
}
}
/**
* Reinitialize selected wallet. Example, scanning a new card if wallets saving is turned off
*

View file

@ -87,7 +87,7 @@ web3j = "4.10.1"
# region Tangem
tangemBlockchainSdk = "release-app_5.8-525"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "release-app_5.8-333"
tangemCardSdk = "release-app_5.8-335"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
# endregion Tangem