Updated on 2026-08-14

This commit is contained in:
Tangem 2025-08-14 14:47:48 +03:00
parent c68cf7165b
commit bea775e94b
46 changed files with 749 additions and 178 deletions

View file

@ -7,6 +7,8 @@ import androidx.work.WorkerParameters
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.legacy.asLockable
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import timber.log.Timber
@ -17,13 +19,22 @@ class LockTimerWorker @AssistedInject constructor(
@Assisted params: WorkerParameters,
private val settingsRepository: SettingsRepository,
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
Timber.i("onStart job")
val userWalletsListManagerLockable = userWalletsListManager.asLockable() ?: return Result.failure()
userWalletsListManagerLockable.lock()
settingsRepository.setShouldOpenWelcomeScreenOnResume(value = true)
if (hotWalletFeatureToggles.isHotWalletEnabled) {
userWalletsListRepository.lockAllWallets()
.onRight {
settingsRepository.setShouldOpenWelcomeScreenOnResume(value = true)
}
} else {
val userWalletsListManagerLockable = userWalletsListManager.asLockable() ?: return Result.failure()
userWalletsListManagerLockable.lock()
settingsRepository.setShouldOpenWelcomeScreenOnResume(value = true)
}
Timber.i("onStart job complete")
return Result.success()
}

View file

@ -10,6 +10,8 @@ import com.tangem.common.routing.AppRoute
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.legacy.asLockable
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.tap.LockTimerWorker.Companion.TAG
import com.tangem.tap.common.extensions.dispatchNavigationAction
import kotlinx.coroutines.CoroutineScope
@ -25,6 +27,8 @@ internal class LockUserWalletsTimer(
private val settingsRepository: SettingsRepository,
private val duration: Duration = with(Duration) { 5.minutes },
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
private val coroutineScope: CoroutineScope,
) : LifecycleOwner by context as LifecycleOwner,
DefaultLifecycleObserver {
@ -108,20 +112,33 @@ internal class LockUserWalletsTimer(
delay(duration)
val userWalletsListManager = userWalletsListManager.asLockable() ?: return@launch
if (hotWalletFeatureToggles.isHotWalletEnabled) {
val userWallets = userWalletsListRepository.userWalletsSync()
if (userWallets.isNotEmpty()) {
userWalletsListRepository.lockAllWallets()
.onLeft {
start()
}
.onRight {
store.dispatchNavigationAction { replaceAll(AppRoute.Welcome()) }
}
}
} else {
val userWalletsListManager = userWalletsListManager.asLockable() ?: return@launch
if (userWalletsListManager.hasUserWallets) {
val currentTime = System.currentTimeMillis()
if (userWalletsListManager.hasUserWallets) {
val currentTime = System.currentTimeMillis()
Timber.i(
"""
Timber.i(
"""
Finished
|- Millis passed: ${currentTime - startTime}
""".trimIndent(),
)
""".trimIndent(),
)
userWalletsListManager.lock()
store.dispatchNavigationAction { replaceAll(AppRoute.Welcome()) }
userWalletsListManager.lock()
store.dispatchNavigationAction { replaceAll(AppRoute.Welcome()) }
}
}
}
}

View file

@ -40,6 +40,7 @@ import com.tangem.domain.apptheme.model.AppThemeMode
import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.settings.SetGooglePayAvailabilityUseCase
import com.tangem.domain.settings.SetGoogleServicesAvailabilityUseCase
import com.tangem.domain.settings.ShouldInitiallyAskPermissionUseCase
@ -48,7 +49,9 @@ import com.tangem.domain.staking.SendUnsubmittedHashesUseCase
import com.tangem.domain.tokens.GetPolkadotCheckHasImmortalUseCase
import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.features.tester.api.TesterMenuLauncher
import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles
import com.tangem.google.GoogleServicesHelper
@ -188,6 +191,12 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
@Inject
internal lateinit var backgroundScanIntentHandler: BackgroundScanIntentHandler
@Inject
internal lateinit var userWalletsListRepository: UserWalletsListRepository
@Inject
internal lateinit var hotWalletFeatureToggles: HotWalletFeatureToggles
internal val viewModel: MainViewModel by viewModels()
private lateinit var appThemeModeFlow: SharedFlow<AppThemeMode>
@ -271,6 +280,8 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
settingsRepository = settingsRepository,
userWalletsListManager = userWalletsListManager,
coroutineScope = mainScope,
userWalletsListRepository = userWalletsListRepository,
hotWalletFeatureToggles = hotWalletFeatureToggles,
)
initIntentHandlers()
@ -429,6 +440,12 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
}
private fun navigateToInitialScreenIfNeeded(intentWhichStartedActivity: Intent?) {
// TODO refactor this method to return a route instead of navigating directly
if (hotWalletFeatureToggles.isHotWalletEnabled) {
navigateToInitialScreenIfNeededNew(intentWhichStartedActivity)
return
}
val backStack = appRouterConfig.stack ?: emptyList()
// TODO move inital navigation to navigation component ([REDACTED_JIRA])
val isOnlyInitialRoute = backStack.all { it is AppRoute.Initial }
@ -448,6 +465,57 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
}
}
@Deprecated("Refactor this method to return a route instead of navigating directly")
private fun navigateToInitialScreenIfNeededNew(intentWhichStartedActivity: Intent?) {
lifecycleScope.launch {
val userWallets = userWalletsListRepository.userWalletsSync()
val launchMode = backgroundScanIntentHandler.getInitScreenLaunchMode(intentWhichStartedActivity)
if (userWallets.isEmpty()) {
val shouldShowTos = !cardRepository.isTangemTOSAccepted()
val route = if (shouldShowTos) {
AppRoute.Disclaimer(isTosAccepted = false)
} else {
AppRoute.Home(launchMode = launchMode)
}
store.dispatchNavigationAction { replaceAll(route) }
intentProcessor.handleIntent(
intent = intentWhichStartedActivity,
isFromForeground = false,
skipNavigationHandlers = false,
)
} else {
if (userWallets.any { it.isLocked }) {
store.dispatchNavigationAction {
replaceAll(
AppRoute.Welcome(
launchMode = launchMode,
intent = intentWhichStartedActivity?.let(::SerializableIntent),
),
)
}
} else {
store.dispatchNavigationAction {
replaceAll(AppRoute.Wallet)
}
}
intentProcessor.handleIntent(
intent = intentWhichStartedActivity,
isFromForeground = false,
skipNavigationHandlers = true,
)
}
if (intent != null) {
handleDeepLink(intent = intent, isFromOnNewIntent = false)
}
viewModel.checkForUnfinishedBackup()
}
}
private fun navigateToInitialScreen(intentWhichStartedActivity: Intent?) {
val launchMode = backgroundScanIntentHandler.getInitScreenLaunchMode(intentWhichStartedActivity)
if (userWalletsListManager.isLockable && userWalletsListManager.hasUserWallets) {

View file

@ -6,7 +6,6 @@ import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
// FIXME: Workaround, remove it once the normal UserWalletsStore has been implemented
// [REDACTED_JIRA]
@ -28,10 +27,6 @@ internal class RuntimeUserWalletsStore(
return requireNotNull(getSyncOrNull(key)) { "Unable to find user wallet with provided ID: $key" }
}
override suspend fun getAllSyncOrNull(): List<UserWallet>? {
return userWalletsListManager.userWallets.firstOrNull()
}
override suspend fun update(
userWalletId: UserWalletId,
update: suspend (UserWallet) -> UserWallet,

View file

@ -0,0 +1,50 @@
package com.tangem.tap.data
import com.tangem.common.CompletionResult
import com.tangem.common.catching
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.core.wallets.UserWalletsListRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
class UserWalletsStoreRepositoryProxy(
private val userWalletsListRepository: UserWalletsListRepository,
) : UserWalletsStore {
override val selectedUserWalletOrNull: UserWallet?
get() = userWalletsListRepository.selectedUserWallet.value
override val userWallets: Flow<List<UserWallet>>
get() = flow {
userWalletsListRepository.load()
userWalletsListRepository.userWallets.collect {
emit(requireNotNull(it))
}
}
override fun getSyncOrNull(key: UserWalletId): UserWallet? {
return userWalletsListRepository.userWallets.value?.find { it.walletId == key }
}
override fun getSyncStrict(key: UserWalletId): UserWallet {
return requireNotNull(getSyncOrNull(key)) { "Unable to find user wallet with provided ID: $key" }
}
override suspend fun update(
userWalletId: UserWalletId,
update: suspend (UserWallet) -> UserWallet,
): CompletionResult<UserWallet> {
return catching {
val userWallet = userWalletsListRepository.userWallets.value?.find { it.walletId == userWalletId }
requireNotNull(userWallet) { "Unable to find user wallet with provided ID: $userWalletId" }
val updatedUserWallet = update(userWallet)
userWalletsListRepository.saveWithoutLock(
userWallet = updatedUserWallet,
canOverride = true,
)
updatedUserWallet
}
}
}

View file

@ -2,7 +2,10 @@ package com.tangem.tap.di.data
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.tap.data.RuntimeUserWalletsStore
import com.tangem.tap.data.UserWalletsStoreRepositoryProxy
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -15,7 +18,15 @@ internal object UserWalletsStoreModule {
@Provides
@Singleton
fun provideUserWalletsStore(userWalletsListManager: UserWalletsListManager): UserWalletsStore {
return RuntimeUserWalletsStore(userWalletsListManager = userWalletsListManager)
fun provideUserWalletsStore(
userWalletsListManager: UserWalletsListManager,
userWalletsListRepository: UserWalletsListRepository,
hotWalletFeatureToggles: HotWalletFeatureToggles,
): UserWalletsStore {
return if (hotWalletFeatureToggles.isHotWalletEnabled) {
UserWalletsStoreRepositoryProxy(userWalletsListRepository)
} else {
RuntimeUserWalletsStore(userWalletsListManager = userWalletsListManager)
}
}
}

View file

@ -7,11 +7,13 @@ import com.tangem.domain.demo.models.DemoConfig
import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase
import com.tangem.domain.wallets.usecase.GetExtendedPublicKeyForCurrencyUseCase
import com.tangem.domain.wallets.usecase.HasMissedDerivationsUseCase
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
import com.tangem.domain.wallets.usecase.NetworkHasDerivationUseCase
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.sdk.api.TangemSdkManager
import com.tangem.tap.domain.card.DefaultDeleteSavedAccessCodesUseCase
import com.tangem.tap.domain.card.DefaultResetCardUseCase
@ -42,9 +44,16 @@ internal object CardDomainModule {
}
@Provides
@Singleton
fun provideIsNeedToBackupUseCase(userWalletsListManager: UserWalletsListManager): IsNeedToBackupUseCase {
return IsNeedToBackupUseCase(userWalletsListManager = userWalletsListManager)
fun provideIsNeedToBackupUseCase(
userWalletsListManager: UserWalletsListManager,
userWalletsListRepository: UserWalletsListRepository,
hotWalletFeatureToggles: HotWalletFeatureToggles,
): IsNeedToBackupUseCase {
return IsNeedToBackupUseCase(
userWalletsListManager = userWalletsListManager,
userWalletsListRepository = userWalletsListRepository,
useNewRepository = hotWalletFeatureToggles.isHotWalletEnabled,
)
}
@Provides

View file

@ -3,7 +3,9 @@ package com.tangem.tap.di.domain
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.tap.domain.scanCard.CardScanningFeatureToggles
import com.tangem.tap.domain.scanCard.DefaultScanCardProcessor
import com.tangem.tap.domain.scanCard.LegacyScanProcessor
@ -31,7 +33,15 @@ internal object CardLegacyDomainModule {
@Provides
@Singleton
fun providesWalletNameGenerateUseCase(userWalletsListManager: UserWalletsListManager): GenerateWalletNameUseCase {
return GenerateWalletNameUseCase(userWalletsListManager)
fun providesWalletNameGenerateUseCase(
userWalletsListManager: UserWalletsListManager,
userWalletsListRepository: UserWalletsListRepository,
hotWalletFeatureToggles: HotWalletFeatureToggles,
): GenerateWalletNameUseCase {
return GenerateWalletNameUseCase(
userWalletsListManager = userWalletsListManager,
userWalletsListRepository = userWalletsListRepository,
useNewRepository = hotWalletFeatureToggles.isHotWalletEnabled,
)
}
}

View file

@ -13,6 +13,8 @@ import com.tangem.domain.staking.StakingIdFactory
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -81,10 +83,14 @@ object MarketsDomainModule {
@Singleton
fun provideFilterNetworksUseCase(
userWalletsListManager: UserWalletsListManager,
userWalletsListRepository: UserWalletsListRepository,
hotWalletFeatureToggles: HotWalletFeatureToggles,
excludedBlockchains: ExcludedBlockchains,
): FilterAvailableNetworksForWalletUseCase {
return FilterAvailableNetworksForWalletUseCase(
userWalletsListManager = userWalletsListManager,
userWalletsListRepository = userWalletsListRepository,
useNewRepository = hotWalletFeatureToggles.isHotWalletEnabled,
excludedBlockchains = excludedBlockchains,
)
}

View file

@ -10,11 +10,13 @@ import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.delegate.DefaultUserWalletsSyncDelegate
import com.tangem.domain.wallets.delegate.UserWalletsSyncDelegate
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository
import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.domain.wallets.usecase.*
import com.tangem.feature.wallet.presentation.wallet.domain.IsWalletNFTEnabledSyncUseCase
import com.tangem.feature.wallet.presentation.wallet.domain.WalletNameMigrationUseCase
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.operations.attestation.CardArtworksProvider
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
@ -31,18 +33,30 @@ internal object WalletsDomainModule {
@Provides
fun providesUserWalletsSyncDelegate(
userWalletsListManager: UserWalletsListManager,
userWalletsListRepository: UserWalletsListRepository,
hotWalletFeatureToggles: HotWalletFeatureToggles,
dispatchers: CoroutineDispatcherProvider,
): UserWalletsSyncDelegate {
return DefaultUserWalletsSyncDelegate(
userWalletsListManager = userWalletsListManager,
userWalletsListRepository = userWalletsListRepository,
useNewRepository = hotWalletFeatureToggles.isHotWalletEnabled,
dispatchers = dispatchers,
)
}
@Provides
@Singleton
fun providesGetWalletsUseCase(userWalletsListManager: UserWalletsListManager): GetWalletsUseCase {
return GetWalletsUseCase(userWalletsListManager = userWalletsListManager)
fun providesGetWalletsUseCase(
userWalletsListManager: UserWalletsListManager,
userWalletsListRepository: UserWalletsListRepository,
hotWalletFeatureToggles: HotWalletFeatureToggles,
): GetWalletsUseCase {
return GetWalletsUseCase(
userWalletsListManager = userWalletsListManager,
userWalletsListRepository = userWalletsListRepository,
useNewListRepository = hotWalletFeatureToggles.isHotWalletEnabled,
)
}
@Provides
@ -50,37 +64,71 @@ internal object WalletsDomainModule {
fun providesWalletNameMigrationUseCase(
userWalletsListManager: UserWalletsListManager,
walletNamesMigrationRepository: WalletNamesMigrationRepository,
userWalletsListRepository: UserWalletsListRepository,
hotWalletFeatureToggles: HotWalletFeatureToggles,
): WalletNameMigrationUseCase {
return WalletNameMigrationUseCase(
userWalletsListManager = userWalletsListManager,
walletNamesMigrationRepository = walletNamesMigrationRepository,
userWalletsListRepository = userWalletsListRepository,
useNewListRepository = hotWalletFeatureToggles.isHotWalletEnabled,
)
}
@Provides
@Singleton
fun providesGetUserWalletUseCase(userWalletsListManager: UserWalletsListManager): GetUserWalletUseCase {
return GetUserWalletUseCase(userWalletsListManager = userWalletsListManager)
fun providesGetUserWalletUseCase(
userWalletsListManager: UserWalletsListManager,
userWalletsListRepository: UserWalletsListRepository,
hotWalletFeatureToggles: HotWalletFeatureToggles,
): GetUserWalletUseCase {
return GetUserWalletUseCase(
userWalletsListManager = userWalletsListManager,
userWalletsListRepository = userWalletsListRepository,
useNewListRepository = hotWalletFeatureToggles.isHotWalletEnabled,
)
}
@Provides
@Singleton
fun providesGetSelectedWalletSyncUseCase(
userWalletsListManager: UserWalletsListManager,
userWalletsListRepository: UserWalletsListRepository,
hotWalletFeatureToggles: HotWalletFeatureToggles,
): GetSelectedWalletSyncUseCase {
return GetSelectedWalletSyncUseCase(userWalletsListManager = userWalletsListManager)
return GetSelectedWalletSyncUseCase(
userWalletsListManager = userWalletsListManager,
userWalletsListRepository = userWalletsListRepository,
useNewRepository = hotWalletFeatureToggles.isHotWalletEnabled,
)
}
@Provides
@Singleton
fun providesGetSelectedWalletUseCase(userWalletsListManager: UserWalletsListManager): GetSelectedWalletUseCase {
return GetSelectedWalletUseCase(userWalletsListManager = userWalletsListManager)
fun providesGetSelectedWalletUseCase(
userWalletsListManager: UserWalletsListManager,
userWalletsListRepository: UserWalletsListRepository,
hotWalletFeatureToggles: HotWalletFeatureToggles,
): GetSelectedWalletUseCase {
return GetSelectedWalletUseCase(
userWalletsListManager = userWalletsListManager,
userWalletsListRepository = userWalletsListRepository,
useNewRepository = hotWalletFeatureToggles.isHotWalletEnabled,
)
}
@Provides
@Singleton
fun providesSaveWalletUseCase(userWalletsListManager: UserWalletsListManager): SaveWalletUseCase {
return SaveWalletUseCase(userWalletsListManager = userWalletsListManager)
fun providesSaveWalletUseCase(
userWalletsListManager: UserWalletsListManager,
userWalletsListRepository: UserWalletsListRepository,
hotWalletFeatureToggles: HotWalletFeatureToggles,
): SaveWalletUseCase {
return SaveWalletUseCase(
userWalletsListManager = userWalletsListManager,
userWalletsListRepository = userWalletsListRepository,
useNewRepository = hotWalletFeatureToggles.isHotWalletEnabled,
)
}
@Provides
@ -99,15 +147,30 @@ internal object WalletsDomainModule {
@Singleton
fun providesSelectWalletUseCase(
userWalletsListManager: UserWalletsListManager,
userWalletsListRepository: UserWalletsListRepository,
hotWalletFeatureToggles: HotWalletFeatureToggles,
reduxStateHolder: ReduxStateHolder,
): SelectWalletUseCase {
return SelectWalletUseCase(userWalletsListManager = userWalletsListManager, reduxStateHolder = reduxStateHolder)
return SelectWalletUseCase(
userWalletsListManager = userWalletsListManager,
userWalletsListRepository = userWalletsListRepository,
useNewRepository = hotWalletFeatureToggles.isHotWalletEnabled,
reduxStateHolder = reduxStateHolder,
)
}
@Provides
@Singleton
fun providesUpdateWalletUseCase(userWalletsListManager: UserWalletsListManager): UpdateWalletUseCase {
return UpdateWalletUseCase(userWalletsListManager = userWalletsListManager)
fun providesUpdateWalletUseCase(
userWalletsListManager: UserWalletsListManager,
userWalletsListRepository: UserWalletsListRepository,
hotWalletFeatureToggles: HotWalletFeatureToggles,
): UpdateWalletUseCase {
return UpdateWalletUseCase(
userWalletsListManager = userWalletsListManager,
userWalletsListRepository = userWalletsListRepository,
useNewRepository = hotWalletFeatureToggles.isHotWalletEnabled,
)
}
@Provides
@ -124,14 +187,30 @@ internal object WalletsDomainModule {
@Provides
@Singleton
fun providesGetWalletsSyncUseCase(userWalletsListManager: UserWalletsListManager): GetWalletNamesUseCase {
return GetWalletNamesUseCase(userWalletsListManager = userWalletsListManager)
fun providesGetWalletsSyncUseCase(
userWalletsListManager: UserWalletsListManager,
userWalletsListRepository: UserWalletsListRepository,
hotWalletFeatureToggles: HotWalletFeatureToggles,
): GetWalletNamesUseCase {
return GetWalletNamesUseCase(
userWalletsListManager = userWalletsListManager,
userWalletsListRepository = userWalletsListRepository,
useNewRepository = hotWalletFeatureToggles.isHotWalletEnabled,
)
}
@Provides
@Singleton
fun providesDeleteWalletUseCase(userWalletsListManager: UserWalletsListManager): DeleteWalletUseCase {
return DeleteWalletUseCase(userWalletsListManager = userWalletsListManager)
fun providesDeleteWalletUseCase(
userWalletsListManager: UserWalletsListManager,
userWalletsListRepository: UserWalletsListRepository,
hotWalletFeatureToggles: HotWalletFeatureToggles,
): DeleteWalletUseCase {
return DeleteWalletUseCase(
userWalletsListManager = userWalletsListManager,
userWalletsListRepository = userWalletsListRepository,
useNewRepository = hotWalletFeatureToggles.isHotWalletEnabled,
)
}
@Provides
@ -214,9 +293,13 @@ internal object WalletsDomainModule {
@Singleton
fun providesGetSavedWalletChangesIdUseCase(
userWalletsListManager: UserWalletsListManager,
userWalletsListRepository: UserWalletsListRepository,
hotWalletFeatureToggles: HotWalletFeatureToggles,
): GetSavedWalletsCountUseCase {
return GetSavedWalletsCountUseCase(
userWalletsListManager = userWalletsListManager,
userWalletsListRepository = userWalletsListRepository,
useNewRepository = hotWalletFeatureToggles.isHotWalletEnabled,
)
}

View file

@ -30,6 +30,7 @@ import com.tangem.tap.domain.userWalletList.utils.toUserWallets
import com.tangem.tap.domain.userWalletList.utils.updateWith
import com.tangem.utils.Provider
import com.tangem.utils.ProviderSuspend
import com.tangem.utils.extensions.indexOfFirstOrNull
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
@ -173,6 +174,8 @@ internal class DefaultUserWalletsListRepository(
userWalletEncryptionKeysRepository.delete(userWalletIds)
val userWalletsBeforeDelete = userWallets.value ?: return@either
userWallets.update { currentWallets ->
currentWallets?.filterNot { it.walletId in userWalletIds }
}
@ -181,7 +184,7 @@ internal class DefaultUserWalletsListRepository(
if (currentSelected == null) return@update null
userWallets.value?.findAvailableUserWallet(
userWallets.value?.indexOfFirst { it.walletId == currentSelected.walletId } ?: 0,
userWalletsBeforeDelete.indexOfFirstOrNull { it.walletId == currentSelected.walletId } ?: 0,
)
}
}
@ -199,7 +202,7 @@ internal class DefaultUserWalletsListRepository(
when (unlockMethod) {
UserWalletsListRepository.UnlockMethod.Biometric -> {
unlockAllWallets()
unlockAllWallets().bind()
select(userWalletId)
}
UserWalletsListRepository.UnlockMethod.AccessCode -> {
@ -225,7 +228,7 @@ internal class DefaultUserWalletsListRepository(
}
sensitiveInformationRepository.getAll(listOf(encryptionKey))
.doOnSuccess { userWallets.value?.updateWith(it) }
.doOnSuccess { sensitiveInfo -> userWallets.update { it?.updateWith(sensitiveInfo) } }
.doOnFailure { error ->
raise(UnlockWalletError.UnableToUnlock)
}
@ -255,6 +258,7 @@ internal class DefaultUserWalletsListRepository(
}
override suspend fun unlockAllWallets(): Either<UnlockWalletError, Unit> = either {
val userWalletIds = userWalletsSync().map { it.walletId }.toSet()
val biometricKeys = runCatching {
userWalletEncryptionKeysRepository.getAllBiometric()
}.getOrElse {
@ -264,8 +268,14 @@ internal class DefaultUserWalletsListRepository(
val unsecuredKeys = userWalletEncryptionKeysRepository.getAllUnsecured()
val allKeys = biometricKeys + unsecuredKeys
if (allKeys.all { it.walletId in userWalletIds }.not()) {
raise(UnlockWalletError.UnableToUnlock)
}
sensitiveInformationRepository.getAll(allKeys)
.doOnSuccess { userWallets.value?.updateWith(it) }
.doOnSuccess { sensitiveInfo -> userWallets.update { it?.updateWith(sensitiveInfo) } }
.doOnFailure { raise(UnlockWalletError.UnableToUnlock) }
}
override suspend fun lockAllWallets(): Either<LockWalletsError, Unit> = either {
@ -297,7 +307,7 @@ internal class DefaultUserWalletsListRepository(
biometryFallback: suspend () -> Either<UnlockWalletError, Unit>,
): Either<UnlockWalletError, UserWalletEncryptionKey?> {
val result = passwordRequester.requestPassword(
hasBiometry = tangemSdkManagerProvider.invoke().needEnrollBiometrics,
hasBiometry = tangemSdkManagerProvider.invoke().canUseBiometry,
)
return when (result) {
@ -312,6 +322,7 @@ internal class DefaultUserWalletsListRepository(
requestPasswordRecursive(block, biometryFallback)
} else {
passwordRequester.successfulAuthentication()
passwordRequester.dismiss()
decrypted.right()
}
}
@ -319,9 +330,9 @@ internal class DefaultUserWalletsListRepository(
biometryFallback()
.onRight {
passwordRequester.successfulAuthentication()
passwordRequester.dismiss()
}
passwordRequester.dismiss()
null.right()
.map { null }
}
}
}

View file

@ -62,15 +62,16 @@ internal class UserWalletEncryptionKeysRepository(
}
}
suspend fun getEncryptedWithPassword(userWalletId: UserWalletId, password: CharArray): UserWalletEncryptionKey? {
val encrypted = secureStorage.get(
account = StorageKey.UserWalletEncryptionKeyEncrypted(userWalletId).name,
) ?: return null
suspend fun getEncryptedWithPassword(userWalletId: UserWalletId, password: CharArray): UserWalletEncryptionKey? =
withContext(dispatchers.io) {
val encrypted = secureStorage.get(
account = StorageKey.UserWalletEncryptionKeyEncrypted(userWalletId).name,
) ?: return@withContext null
val decrypted = AESEncryptionProtocol.decryptWithPassword(password, encrypted)
return decrypted.decodeToKey()
}
withContext(dispatchers.default) {
AESEncryptionProtocol.decryptWithPassword(password, encrypted).decodeToKey()
}
}
suspend fun getAllBiometric(): List<UserWalletEncryptionKey> = withContext(dispatchers.io) {
val keys = getUserWalletsIds().map { userWalletId ->

View file

@ -11,7 +11,7 @@ import com.tangem.domain.walletconnect.WcPairService
import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository
import com.tangem.domain.walletconnect.usecase.initialize.WcInitializeUseCase
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles
import com.tangem.tap.domain.walletconnect.WalletConnectSdkHelper
import com.tangem.tap.domain.walletconnect2.app.TangemWcBlockchainHelper
@ -42,9 +42,9 @@ internal object WalletConnectInteractorModule {
wcSessionsRepository: WalletConnectSessionsRepository,
currenciesRepository: CurrenciesRepository,
walletManagersFacade: WalletManagersFacade,
userWalletsListManager: UserWalletsListManager,
walletConnectFeatureToggles: WalletConnectFeatureToggles,
coroutineDispatcherProvider: CoroutineDispatcherProvider,
getSelectedWalletUseCase: GetSelectedWalletUseCase,
): WalletConnectInteractor {
return WalletConnectInteractor(
handler = WalletConnectEventsHandlerImpl(),
@ -54,7 +54,7 @@ internal object WalletConnectInteractorModule {
blockchainHelper = TangemWcBlockchainHelper(),
currenciesRepository = currenciesRepository,
walletManagersFacade = walletManagersFacade,
userWalletsListManager = userWalletsListManager,
getSelectedWalletUseCase = getSelectedWalletUseCase,
dispatchers = coroutineDispatcherProvider,
walletConnectFeatureToggles = walletConnectFeatureToggles,
)

View file

@ -13,7 +13,6 @@ import com.tangem.domain.walletconnect.model.legacy.Account
import com.tangem.domain.walletconnect.model.legacy.Session
import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles
import com.tangem.tap.common.extensions.dispatchOnMain
@ -38,18 +37,14 @@ class WalletConnectInteractor(
private val dispatchers: CoroutineDispatcherProvider,
private val walletManagersFacade: WalletManagersFacade,
private val currenciesRepository: CurrenciesRepository,
private val userWalletsListManager: UserWalletsListManager,
private val walletConnectFeatureToggles: WalletConnectFeatureToggles,
private val getSelectedWalletUseCase: GetSelectedWalletUseCase,
val blockchainHelper: WcBlockchainHelper,
) {
private val isNewWc by lazy { walletConnectFeatureToggles.isRedesignedWalletConnectEnabled }
private var isWalletConnectReadyForDeepLinks = false
private val getSelectedWalletUseCase by lazy(LazyThreadSafetyMode.NONE) {
GetSelectedWalletUseCase(userWalletsListManager)
}
private val wcScope = CoroutineScope(
SupervisorJob() + dispatchers.io + CoroutineExceptionHandler { _, throwable ->
Timber.e("CoroutineException: from: LISTENER SCOPE, exception: $throwable")

View file

@ -4,11 +4,16 @@ import com.tangem.common.extensions.toHexString
import com.tangem.datasource.api.common.AuthProvider
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.core.wallets.UserWalletsListRepository
internal class DefaultAuthProvider(private val userWalletsListManager: UserWalletsListManager) : AuthProvider {
internal class DefaultAuthProvider(
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val useNewListRepository: Boolean = false,
) : AuthProvider {
override fun getCardPublicKey(): String {
val userWallet = userWalletsListManager.selectedUserWalletSync
override suspend fun getCardPublicKey(): String {
val userWallet = getSelectedWallet()
if (userWallet !is UserWallet.Cold) {
return ""
@ -17,8 +22,8 @@ internal class DefaultAuthProvider(private val userWalletsListManager: UserWalle
return userWallet.scanResponse.card.cardPublicKey.toHexString()
}
override fun getCardId(): String {
val userWallet = userWalletsListManager.selectedUserWalletSync
override suspend fun getCardId(): String {
val userWallet = getSelectedWallet()
if (userWallet !is UserWallet.Cold) {
return ""
@ -27,9 +32,25 @@ internal class DefaultAuthProvider(private val userWalletsListManager: UserWalle
return userWallet.scanResponse.card.cardId
}
override fun getCardsPublicKeys(): Map<String, String> {
return userWalletsListManager.userWalletsSync.filterIsInstance<UserWallet.Cold>().associate {
override suspend fun getCardsPublicKeys(): Map<String, String> {
return getWallets().filterIsInstance<UserWallet.Cold>().associate {
it.scanResponse.card.cardId to it.scanResponse.card.cardPublicKey.toHexString()
}
}
private suspend fun getWallets(): List<UserWallet> {
return if (useNewListRepository) {
userWalletsListRepository.userWalletsSync()
} else {
userWalletsListManager.userWalletsSync
}
}
private suspend fun getSelectedWallet(): UserWallet? {
return if (useNewListRepository) {
userWalletsListRepository.selectedUserWalletSync()
} else {
userWalletsListManager.selectedUserWalletSync
}
}
}

View file

@ -3,6 +3,8 @@ package com.tangem.tap.network.auth.di
import com.tangem.datasource.api.common.AuthProvider
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.lib.auth.ExpressAuthProvider
import com.tangem.lib.auth.StakeKitAuthProvider
import com.tangem.tap.network.auth.DefaultAppVersionProvider
@ -22,8 +24,16 @@ internal class AuthModule {
@Provides
@Singleton
fun provideAuthProvider(userWalletsListManager: UserWalletsListManager): AuthProvider {
return DefaultAuthProvider(userWalletsListManager)
fun provideAuthProvider(
userWalletsListManager: UserWalletsListManager,
userWalletsListRepository: UserWalletsListRepository,
hotWalletFeatureToggles: HotWalletFeatureToggles,
): AuthProvider {
return DefaultAuthProvider(
userWalletsListManager = userWalletsListManager,
userWalletsListRepository = userWalletsListRepository,
useNewListRepository = hotWalletFeatureToggles.isHotWalletEnabled,
)
}
@Provides

View file

@ -14,6 +14,7 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.runBlocking
import javax.inject.Singleton
@Module
@ -41,6 +42,12 @@ internal object FeatureTogglesManagerModule {
localTogglesStorage = localTogglesStorage,
versionProvider = versionProvider,
)
}.also {
// We need to initialize during the hilt graph creation
// in order to provide the feature toggles correctly to other dependencies.
runBlocking {
it.init()
}
}
}
}

View file

@ -9,7 +9,6 @@ import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
import com.tangem.datasource.local.preferences.utils.storeObject
import kotlin.properties.Delegates
/**
* Feature toggles manager implementation in DEV build
@ -24,10 +23,14 @@ internal class DevFeatureTogglesManager(
private val versionProvider: VersionProvider,
) : MutableFeatureTogglesManager {
private var featureTogglesMap: MutableMap<String, Boolean> by Delegates.notNull()
private var localFeatureTogglesMap: Map<String, Boolean> by Delegates.notNull()
private var featureTogglesMap: MutableMap<String, Boolean>? = null
private var localFeatureTogglesMap: Map<String, Boolean>? = null
override suspend fun init() {
if (featureTogglesMap != null && localFeatureTogglesMap != null) {
return // Already initialized
}
localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH)
val savedFeatureToggles = appPreferencesStore.getObjectSyncOrNull<Map<String, Boolean>>(
@ -46,21 +49,21 @@ internal class DevFeatureTogglesManager(
.toMutableMap()
}
override fun isFeatureEnabled(name: String): Boolean = featureTogglesMap[name] ?: false
override fun isFeatureEnabled(name: String): Boolean = featureTogglesMap!![name] ?: false
override fun isMatchLocalConfig(): Boolean = featureTogglesMap == localFeatureTogglesMap
override fun getFeatureToggles(): Map<String, Boolean> = featureTogglesMap
override fun getFeatureToggles(): Map<String, Boolean> = featureTogglesMap!!
override suspend fun changeToggle(name: String, isEnabled: Boolean) {
featureTogglesMap[name] ?: return
featureTogglesMap[name] = isEnabled
appPreferencesStore.storeFeatureToggles(value = featureTogglesMap)
featureTogglesMap!![name] ?: return
featureTogglesMap!![name] = isEnabled
appPreferencesStore.storeFeatureToggles(value = featureTogglesMap!!)
}
override suspend fun recoverLocalConfig() {
featureTogglesMap = localFeatureTogglesMap.toMutableMap()
appPreferencesStore.storeFeatureToggles(value = localFeatureTogglesMap)
featureTogglesMap = localFeatureTogglesMap!!.toMutableMap()
appPreferencesStore.storeFeatureToggles(value = localFeatureTogglesMap!!)
}
@VisibleForTesting(otherwise = VisibleForTesting.NONE)

View file

@ -5,7 +5,6 @@ import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.core.configtoggle.storage.TogglesStorage
import com.tangem.core.configtoggle.utils.associateToggles
import com.tangem.core.configtoggle.version.VersionProvider
import kotlin.properties.Delegates
/**
* Feature toggles manager implementation in PROD build
@ -18,18 +17,22 @@ internal class ProdFeatureTogglesManager(
private val versionProvider: VersionProvider,
) : FeatureTogglesManager {
private var featureToggles: Map<String, Boolean> by Delegates.notNull()
private var featureToggles: Map<String, Boolean>? = null
override suspend fun init() {
if (featureToggles != null) {
return // Already initialized
}
localTogglesStorage.populate(FeatureTogglesConstants.LOCAL_CONFIG_PATH)
featureToggles = localTogglesStorage.toggles
.associateToggles(currentVersion = versionProvider.get() ?: "")
}
override fun isFeatureEnabled(name: String): Boolean = featureToggles[name] ?: false
override fun isFeatureEnabled(name: String): Boolean = featureToggles!![name] ?: false
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
fun getProdFeatureToggles() = featureToggles
fun getProdFeatureToggles() = featureToggles!!
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
fun setProdFeatureToggles(map: Map<String, Boolean>) {

View file

@ -8,12 +8,12 @@ interface AuthProvider {
/**
* Returns authToken for tangem tech api
*/
fun getCardPublicKey(): String
suspend fun getCardPublicKey(): String
fun getCardId(): String
suspend fun getCardId(): String
/**
* Returns map where keys(cardId) associated with cardPublicKey
*/
fun getCardsPublicKeys(): Map<String, String>
suspend fun getCardsPublicKeys(): Map<String, String>
}

View file

@ -5,6 +5,10 @@ import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import kotlinx.coroutines.flow.Flow
@Deprecated(
message = "Use UserWalletsListRepository instead",
replaceWith = ReplaceWith("UserWalletsListRepository"),
)
interface UserWalletsStore {
val selectedUserWalletOrNull: UserWallet?
@ -15,8 +19,6 @@ interface UserWalletsStore {
fun getSyncStrict(key: UserWalletId): UserWallet
suspend fun getAllSyncOrNull(): List<UserWallet>?
suspend fun update(
userWalletId: UserWalletId,
update: suspend (UserWallet) -> UserWallet,

View file

@ -18,6 +18,7 @@ import com.tangem.utils.ProviderSuspend
import com.tangem.utils.info.AppInfoProvider
import com.tangem.utils.version.AppVersionProvider
import io.mockk.clearMocks
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.runBlocking
@ -55,8 +56,8 @@ internal class ProdApiConfigsManagerTest {
every { appVersionProvider.versionName } returns VERSION_NAME
every { expressAuthProvider.getSessionId() } returns EXPRESS_SESSION_ID
every { stakeKitAuthProvider.getApiKey() } returns STAKE_KIT_API_KEY
every { appAuthProvider.getCardId() } returns APP_CARD_ID
every { appAuthProvider.getCardPublicKey() } returns APP_CARD_PUBLIC_KEY
coEvery { appAuthProvider.getCardId() } returns APP_CARD_ID
coEvery { appAuthProvider.getCardPublicKey() } returns APP_CARD_PUBLIC_KEY
every { appInfoProvider.osVersion } returns "Android 16"
}

View file

@ -195,7 +195,7 @@ class DefaultWalletsRepositoryTest {
)
val authProvider = mockk<AuthProvider> {
every { getCardsPublicKeys() } returns publicKeys
coEvery { getCardsPublicKeys() } returns publicKeys
}
repository = DefaultWalletsRepository(

View file

@ -6,9 +6,13 @@ import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.domain.card.common.extensions.supportedBlockchains
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.domain.core.wallets.requireUserWalletsSync
class FilterAvailableNetworksForWalletUseCase(
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val useNewRepository: Boolean,
private val excludedBlockchains: ExcludedBlockchains,
) {
@ -20,7 +24,7 @@ class FilterAvailableNetworksForWalletUseCase(
userWalletId: UserWalletId,
networks: Set<TokenMarketInfo.Network>,
): Set<TokenMarketInfo.Network> {
val userWallet = userWalletsListManager.userWalletsSync.firstOrNull {
val userWallet = getWallets().firstOrNull {
it.walletId == userWalletId
} ?: return networks.toSet()
@ -33,4 +37,10 @@ class FilterAvailableNetworksForWalletUseCase(
supportedBlockchains.contains(blockchain)
}.toSet()
}
private fun getWallets() = if (useNewRepository) {
userWalletsListRepository.requireUserWalletsSync()
} else {
userWalletsListManager.userWalletsSync
}
}

View file

@ -10,11 +10,14 @@ import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.models.UserWalletRemoteInfo
import com.tangem.domain.models.wallet.copy
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
class DefaultUserWalletsSyncDelegate(
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val useNewRepository: Boolean,
private val dispatchers: CoroutineDispatcherProvider,
) : UserWalletsSyncDelegate {
@ -28,10 +31,43 @@ class DefaultUserWalletsSyncDelegate(
}
}
// TODO remove dispatchers whnen UserWalletsListManager will be main safe
private suspend fun renameUserWallet(
userWalletId: UserWalletId,
name: String,
): Either<UpdateWalletError, UserWallet> = if (useNewRepository) {
renameUserWalletInNewRepository(userWalletId, name)
} else {
renameUserWalletInLegacyRepository(userWalletId, name)
}
private suspend fun renameUserWalletInNewRepository(
userWalletId: UserWalletId,
name: String,
): Either<UpdateWalletError, UserWallet> = either {
val userWallets = userWalletsListRepository.userWalletsSync()
val userWallet = userWallets.find { it.walletId == userWalletId }
?: raise(UpdateWalletError.DataError(IllegalStateException("User wallet with id $userWalletId not found")))
ensure(userWallets.none { it.name == name && it.walletId != userWalletId }) {
UpdateWalletError.NameAlreadyExists
}
ensure(name != userWallet.name) {
UpdateWalletError.NameAlreadyExists
}
val updatedWallet = userWallet.copy(name = name)
userWalletsListRepository.saveWithoutLock(updatedWallet, canOverride = true)
.map { updatedWallet }
.mapLeft { error -> UpdateWalletError.DataError(IllegalStateException("")) }
.bind()
}
// TODO remove dispatchers whnen UserWalletsListManager will be main safe
private suspend fun renameUserWalletInLegacyRepository(
userWalletId: UserWalletId,
name: String,
): Either<UpdateWalletError, UserWallet> = withContext(dispatchers.io) {
either {
val existingNames = userWalletsListManager.userWalletsSync

View file

@ -1,6 +0,0 @@
package com.tangem.domain.wallets.models
sealed interface SelectWalletError {
object UnableToSelectUserWallet : SelectWalletError
}

View file

@ -6,6 +6,7 @@ import com.tangem.common.doOnFailure
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.core.wallets.error.DeleteWalletError
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.core.wallets.UserWalletsListRepository
/**
* Use case for deleting user wallet
@ -14,7 +15,11 @@ import com.tangem.domain.models.wallet.UserWalletId
*
[REDACTED_AUTHOR]
*/
class DeleteWalletUseCase(private val userWalletsListManager: UserWalletsListManager) {
class DeleteWalletUseCase(
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val useNewRepository: Boolean,
) {
/**
* Deletes user wallet with provided ID.
@ -24,6 +29,12 @@ class DeleteWalletUseCase(private val userWalletsListManager: UserWalletsListMan
* @return [Either] with [DeleteWalletError] or [Boolean] which indicates that there are still saved wallets.
* */
suspend operator fun invoke(userWalletId: UserWalletId): Either<DeleteWalletError, Boolean> {
if (useNewRepository) {
return userWalletsListRepository.delete(userWalletIds = listOf(userWalletId)).map {
userWalletsListRepository.selectedUserWallet.value != null
}
}
return either {
userWalletsListManager.delete(userWalletIds = listOf(userWalletId))
.doOnFailure {

View file

@ -2,12 +2,16 @@ package com.tangem.domain.wallets.usecase
import com.tangem.domain.models.scan.ProductType
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.domain.core.wallets.requireUserWalletsSync
/**
* Use case for user wallet name generation
*/
class GenerateWalletNameUseCase(
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val useNewRepository: Boolean,
) {
operator fun invoke(productType: ProductType, isBackupNotAllowed: Boolean, isStartToCoin: Boolean): String {
@ -17,16 +21,24 @@ class GenerateWalletNameUseCase(
isStartToCoin = isStartToCoin,
)
val existingNames = userWalletsListManager.userWalletsSync.map { it.name }.toSet()
val existingNames = getNamesSet()
return suggestedWalletName(defaultName, existingNames)
}
fun invokeForHot(): String {
val defaultName = "Wallet"
val existingNames = userWalletsListManager.userWalletsSync.map { it.name }.toSet()
val existingNames = getNamesSet()
return suggestedWalletName(defaultName, existingNames)
}
private fun getNamesSet(): Set<String> {
return if (useNewRepository) {
userWalletsListRepository.requireUserWalletsSync().map { it.name }.toSet()
} else {
userWalletsListManager.userWalletsSync.map { it.name }.toSet()
}
}
private fun suggestedWalletName(defaultName: String, existingNames: Set<String>): String {
val startIndex = 2
if (!existingNames.contains(defaultName)) {

View file

@ -4,13 +4,20 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.legacy.asLockable
import com.tangem.domain.wallets.legacy.isLockedSync
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.core.wallets.UserWalletsListRepository
import kotlinx.coroutines.flow.*
class GetSavedWalletsCountUseCase(
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val useNewRepository: Boolean,
) {
operator fun invoke(): Flow<List<UserWallet>> {
if (useNewRepository) {
return userWalletsListRepository.userWallets.map { requireNotNull(it) }
}
return userWalletsListManager.savedWalletsCount
.filter { count ->
if (count == 0) return@filter true

View file

@ -6,6 +6,7 @@ import arrow.core.raise.ensureNotNull
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.GetUserWalletError
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.core.wallets.UserWalletsListRepository
/**
* Use case for getting selected wallet.
@ -15,10 +16,20 @@ import com.tangem.domain.models.wallet.UserWallet
*
[REDACTED_AUTHOR]
*/
class GetSelectedWalletSyncUseCase(private val userWalletsListManager: UserWalletsListManager) {
class GetSelectedWalletSyncUseCase(
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val useNewRepository: Boolean = false,
) {
@Deprecated("You should provide the selected wallet via routing parameters due to the scalability of the features")
operator fun invoke(): Either<GetUserWalletError, UserWallet> {
if (useNewRepository) {
return either {
userWalletsListRepository.selectedUserWallet.value ?: raise(GetUserWalletError.UserWalletNotFound)
}
}
return either {
ensureNotNull(
value = userWalletsListManager.selectedUserWalletSync,

View file

@ -5,7 +5,9 @@ import arrow.core.raise.either
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.GetUserWalletError
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.core.wallets.UserWalletsListRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filterNotNull
/**
* Use case for getting flow of selected wallet.
@ -14,12 +16,32 @@ import kotlinx.coroutines.flow.Flow
*
[REDACTED_AUTHOR]
*/
class GetSelectedWalletUseCase(private val userWalletsListManager: UserWalletsListManager) {
@Deprecated("You should provide the selected wallet via routing parameters due to the scalability of the features")
class GetSelectedWalletUseCase(
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val useNewRepository: Boolean = false,
) {
@Deprecated("You should provide the selected wallet via routing parameters due to the scalability of the features")
operator fun invoke(): Either<GetUserWalletError, Flow<UserWallet>> {
return either {
userWalletsListManager.selectedUserWallet
if (useNewRepository) {
userWalletsListRepository.selectedUserWallet.filterNotNull()
} else {
userWalletsListManager.selectedUserWallet
}
}
}
@Deprecated("You should provide the selected wallet via routing parameters due to the scalability of the features")
fun sync(): Either<GetUserWalletError, UserWallet?> {
return either {
if (useNewRepository) {
userWalletsListRepository.selectedUserWallet.value
} else {
userWalletsListManager.selectedUserWalletSync
}
}
}
}

View file

@ -10,13 +10,24 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.GetUserWalletError
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.domain.core.wallets.requireUserWalletsSync
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.transformLatest
class GetUserWalletUseCase(private val userWalletsListManager: UserWalletsListManager) {
class GetUserWalletUseCase(
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val useNewListRepository: Boolean,
) {
operator fun invoke(userWalletId: UserWalletId): Either<GetUserWalletError, UserWallet> = either {
val userWallets = userWalletsListManager.userWalletsSync
val userWallets = if (useNewListRepository) {
userWalletsListRepository.requireUserWalletsSync()
} else {
userWalletsListManager.userWalletsSync
}
ensureNotNull(userWallets.firstOrNull { it.walletId == userWalletId }) {
raise(GetUserWalletError.UserWalletNotFound)
@ -25,7 +36,13 @@ class GetUserWalletUseCase(private val userWalletsListManager: UserWalletsListMa
@OptIn(ExperimentalCoroutinesApi::class)
fun invokeFlow(userWalletId: UserWalletId): EitherFlow<GetUserWalletError, UserWallet> {
return userWalletsListManager.userWallets.transformLatest { userWallets ->
val flow = if (useNewListRepository) {
userWalletsListRepository.userWallets.map { requireNotNull(it) }
} else {
userWalletsListManager.userWallets
}
return flow.transformLatest { userWallets ->
userWallets.firstOrNull { it.walletId == userWalletId }
?.let { emit(it.right()) }
?: emit(GetUserWalletError.UserWalletNotFound.left())

View file

@ -1,13 +1,23 @@
package com.tangem.domain.wallets.usecase
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.domain.core.wallets.requireUserWalletsSync
/**
* Use case for getting list of user wallets names.
*
* @property userWalletsListManager user wallets list manager
*/
class GetWalletNamesUseCase(private val userWalletsListManager: UserWalletsListManager) {
class GetWalletNamesUseCase(
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val useNewRepository: Boolean,
) {
operator fun invoke(): List<String> = userWalletsListManager.userWalletsSync.map { it.name }
operator fun invoke(): List<String> = if (useNewRepository) {
userWalletsListRepository.requireUserWalletsSync().map { it.name }
} else {
userWalletsListManager.userWalletsSync.map { it.name }
}
}

View file

@ -1,8 +1,10 @@
package com.tangem.domain.wallets.usecase
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.core.wallets.UserWalletsListRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
/**
* Use case for getting list of user wallets
@ -11,11 +13,23 @@ import kotlinx.coroutines.flow.Flow
*
[REDACTED_AUTHOR]
*/
class GetWalletsUseCase(private val userWalletsListManager: UserWalletsListManager) {
class GetWalletsUseCase(
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val useNewListRepository: Boolean,
) {
@Throws(IllegalArgumentException::class)
operator fun invoke(): Flow<List<UserWallet>> = userWalletsListManager.userWallets
operator fun invoke(): Flow<List<UserWallet>> = if (useNewListRepository) {
userWalletsListRepository.userWallets.map { requireNotNull(it) }
} else {
userWalletsListManager.userWallets
}
@Throws(IllegalArgumentException::class)
fun invokeSync(): List<UserWallet> = userWalletsListManager.userWalletsSync
fun invokeSync(): List<UserWallet> = if (useNewListRepository) {
userWalletsListRepository.userWallets.value!!
} else {
userWalletsListManager.userWalletsSync
}
}

View file

@ -4,6 +4,7 @@ import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.core.wallets.UserWalletsListRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
@ -12,12 +13,22 @@ import kotlinx.coroutines.flow.map
*
* @property userWalletsListManager user wallets list manager
*/
class IsNeedToBackupUseCase(private val userWalletsListManager: UserWalletsListManager) {
class IsNeedToBackupUseCase(
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val useNewRepository: Boolean,
) {
operator fun invoke(id: UserWalletId): Flow<Boolean> {
return userWalletsListManager.userWallets
val userWalletsFlow = if (useNewRepository) {
userWalletsListRepository.userWallets
} else {
userWalletsListManager.userWallets
}
return userWalletsFlow
.map { wallets ->
val wallet = wallets.firstOrNull { it.walletId == id }
val wallet = wallets?.firstOrNull { it.walletId == id }
if (wallet == null) {
false
} else {

View file

@ -10,6 +10,7 @@ import com.tangem.domain.wallets.legacy.UserWalletsListError
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.core.wallets.error.SaveWalletError
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.core.wallets.UserWalletsListRepository
/**
* Use case for saving user wallet
@ -18,22 +19,51 @@ import com.tangem.domain.models.wallet.UserWallet
*
[REDACTED_AUTHOR]
*/
class SaveWalletUseCase(private val userWalletsListManager: UserWalletsListManager) {
class SaveWalletUseCase(
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val useNewRepository: Boolean,
) {
suspend operator fun invoke(userWallet: UserWallet, canOverride: Boolean = false): Either<SaveWalletError, Unit> {
return either {
userWalletsListManager.save(userWallet, canOverride)
.doOnSuccess { return Unit.right() }
.doOnFailure {
return when (it) {
is UserWalletsListError.WalletAlreadySaved -> SaveWalletError.WalletAlreadySaved(
it.messageResId,
)
else -> SaveWalletError.DataError(it.messageResId)
}.left()
}
return if (useNewRepository) {
either {
val newUserWallet =
userWalletsListRepository.userWalletsSync().none { it.walletId == userWallet.walletId }
val userWallet = userWalletsListRepository.saveWithoutLock(userWallet, canOverride).bind()
return Unit.right()
if (newUserWallet) {
when (userWallet) {
is UserWallet.Cold -> {
userWalletsListRepository.setLock(
userWallet.walletId,
UserWalletsListRepository.LockMethod.Biometric,
)
}
is UserWallet.Hot -> {
userWalletsListRepository.setLock(
userWallet.walletId,
UserWalletsListRepository.LockMethod.NoLock,
)
}
}.mapLeft { SaveWalletError.DataError(null) }.bind()
}
}
} else {
either {
userWalletsListManager.save(userWallet, canOverride)
.doOnSuccess { return Unit.right() }
.doOnFailure {
return when (it) {
is UserWalletsListError.WalletAlreadySaved -> SaveWalletError.WalletAlreadySaved(
it.messageResId,
)
else -> SaveWalletError.DataError(it.messageResId)
}.left()
}
return Unit.right()
}
}
}
}

View file

@ -6,9 +6,10 @@ import arrow.core.right
import com.tangem.common.CompletionResult
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.SelectWalletError
import com.tangem.domain.core.wallets.error.SelectWalletError
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.core.wallets.UserWalletsListRepository
/**
* Use case for selecting wallet
@ -20,10 +21,19 @@ import com.tangem.domain.models.wallet.UserWalletId
*/
class SelectWalletUseCase(
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val useNewRepository: Boolean,
private val reduxStateHolder: ReduxStateHolder,
) {
suspend operator fun invoke(userWalletId: UserWalletId): Either<SelectWalletError, UserWallet> {
if (useNewRepository) {
return userWalletsListRepository.select(userWalletId).map {
reduxStateHolder.onUserWalletSelected(it)
it
}
}
return either {
return when (val result = userWalletsListManager.select(userWalletId)) {
is CompletionResult.Failure -> raise(SelectWalletError.UnableToSelectUserWallet)

View file

@ -7,6 +7,9 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.UpdateWalletError
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.core.wallets.error.SaveWalletError
import com.tangem.domain.wallets.models.UpdateWalletError.*
import com.tangem.domain.core.wallets.UserWalletsListRepository
/**
* Use case for updating user wallet
@ -15,15 +18,38 @@ import com.tangem.domain.models.wallet.UserWalletId
*
[REDACTED_AUTHOR]
*/
class UpdateWalletUseCase(private val userWalletsListManager: UserWalletsListManager) {
class UpdateWalletUseCase(
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val useNewRepository: Boolean,
) {
suspend operator fun invoke(
userWalletId: UserWalletId,
update: suspend (UserWallet) -> UserWallet,
): Either<UpdateWalletError, UserWallet> = either {
when (val result = userWalletsListManager.update(userWalletId, update)) {
is CompletionResult.Failure -> raise(UpdateWalletError.DataError(result.error))
is CompletionResult.Success -> result.data
): Either<UpdateWalletError, UserWallet> {
if (useNewRepository) {
val userWallet = userWalletsListRepository.userWallets.value?.find { it.walletId == userWalletId }
?: return Either.Left(
UpdateWalletError.DataError(IllegalStateException("User wallet with id $userWalletId not found")),
)
val updatedWallet = update(userWallet)
return userWalletsListRepository.saveWithoutLock(updatedWallet, canOverride = true)
.mapLeft {
when (it) {
is SaveWalletError.DataError -> DataError(
IllegalStateException("Failed to update wallet: ${it.messageId}"),
)
is SaveWalletError.WalletAlreadySaved -> UpdateWalletError.NameAlreadyExists
}
}
}
return either {
when (val result = userWalletsListManager.update(userWalletId, update)) {
is CompletionResult.Failure -> raise(UpdateWalletError.DataError(result.error))
is CompletionResult.Success -> result.data
}
}
}
}

View file

@ -22,7 +22,11 @@ class GetSavedWalletsCountUseCaseTest {
@Before
fun setup() {
userWalletsListManager = mockk()
useCase = GetSavedWalletsCountUseCase(userWalletsListManager)
useCase = GetSavedWalletsCountUseCase(
userWalletsListManager,
userWalletsListRepository = mockk(),
useNewRepository = false,
)
mockkStatic("com.tangem.domain.wallets.legacy.UserWalletsListManagerExtensionsKt")
}

View file

@ -16,8 +16,8 @@ import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.settings.SetSaveWalletScreenShownUseCase
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
import com.tangem.features.biometry.AskBiometryComponent
import com.tangem.features.biometry.impl.ui.state.AskBiometryUM
import com.tangem.sdk.api.TangemSdkManager
@ -40,7 +40,7 @@ internal class AskBiometryModel @Inject constructor(
private val setSaveWalletScreenShownUseCase: SetSaveWalletScreenShownUseCase,
private val settingsRepository: SettingsRepository,
private val tangemSdkManager: TangemSdkManager,
private val userWalletsListManager: UserWalletsListManager,
private val getSelectedWalletUseCase: GetSelectedWalletUseCase,
private val walletsRepository: WalletsRepository,
private val cardSdkConfigRepository: CardSdkConfigRepository,
private val settingsManager: SettingsManager,
@ -87,7 +87,7 @@ internal class AskBiometryModel @Inject constructor(
* because it will be automatically saved on UserWalletsListManager switch
*/
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync ?: run {
val selectedUserWallet = getSelectedWalletUseCase.sync().getOrNull() ?: run {
Timber.e("Unable to save user wallet")
uiMessageSender.send(
SnackbarMessage(stringReference("No selected user wallet")),

View file

@ -21,6 +21,7 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
import com.tangem.features.onboarding.v2.common.ui.CantLeaveBackupDialog
import com.tangem.features.onboarding.v2.impl.R
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
@ -51,6 +52,7 @@ internal class MultiWalletFinalizeModel @Inject constructor(
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory,
private val userWalletsListManager: UserWalletsListManager,
private val saveWalletUseCase: SaveWalletUseCase,
private val cardRepository: CardRepository,
private val onboardingRepository: OnboardingRepository,
private val walletsRepository: WalletsRepository,
@ -231,7 +233,7 @@ internal class MultiWalletFinalizeModel @Inject constructor(
OnboardingMultiWalletComponent.Mode.Onboarding,
OnboardingMultiWalletComponent.Mode.ContinueFinalize,
-> {
userWalletsListManager.save(
saveWalletUseCase(
userWallet = userWalletCreated.copy(
scanResponse = scanResponse.updateScanResponseAfterBackup(),
),
@ -247,13 +249,11 @@ internal class MultiWalletFinalizeModel @Inject constructor(
}
?: userWalletCreated
userWalletsListManager.update(
userWalletId = userWallet.walletId,
update = { wallet ->
wallet.requireColdWallet().copy(
scanResponse = scanResponse.updateScanResponseAfterBackup(),
)
},
saveWalletUseCase(
userWallet = userWallet.requireColdWallet().copy(
scanResponse = scanResponse.updateScanResponseAfterBackup(),
),
canOverride = true,
)
userWallet

View file

@ -43,7 +43,8 @@ import com.tangem.domain.tokens.model.analytics.TokenReceiveAnalyticsEvent
import com.tangem.domain.tokens.wallet.WalletBalanceFetcher
import com.tangem.domain.wallets.builder.ColdUserWalletBuilder
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.usecase.DeleteWalletUseCase
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
import com.tangem.features.onboarding.v2.common.analytics.OnboardingEvent
import com.tangem.features.onboarding.v2.common.ui.interruptBackupDialog
import com.tangem.features.onboarding.v2.impl.R
@ -73,7 +74,8 @@ internal class OnboardingTwinModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
paramsContainer: ParamsContainer,
private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory,
private val userWalletsListManager: UserWalletsListManager,
private val saveWalletUseCase: SaveWalletUseCase,
private val deleteWalletUseCase: DeleteWalletUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
private val saveTwinsOnboardingShownUseCase: SaveTwinsOnboardingShownUseCase,
private val tangemSdkManager: TangemSdkManager,
@ -199,9 +201,9 @@ internal class OnboardingTwinModel @Inject constructor(
// remove wallet only after first step of retwin
if (params.mode == Mode.RecreateWallet) {
userWalletsListManager.delete(
listOfNotNull(UserWalletIdBuilder.scanResponse(params.scanResponse).build()),
)
UserWalletIdBuilder.scanResponse(params.scanResponse).build()?.let {
deleteWalletUseCase(it)
}
}
analyticsEventHandler.send(OnboardingEvent.CreateWallet.WalletCreatedSuccessfully())
@ -329,7 +331,14 @@ internal class OnboardingTwinModel @Inject constructor(
return@coroutineScope
}
userWalletsListManager.save(userWallet, canOverride = true)
saveWalletUseCase(
userWallet = userWallet,
canOverride = true,
).onLeft {
Timber.e("Unable to save user wallet: $it")
setLoading(false)
return@coroutineScope
}
cardRepository.finishCardActivation(params.scanResponse.card.cardId)
@ -456,7 +465,15 @@ internal class OnboardingTwinModel @Inject constructor(
return@launch
}
userWalletsListManager.save(userWallet, canOverride = true)
saveWalletUseCase(
userWallet = userWallet,
canOverride = true,
).onLeft {
Timber.e("Unable to save user wallet: $it")
setLoading(false)
return@launch
}
params.modelCallbacks.onDone()
}
}

View file

@ -20,8 +20,8 @@ import com.tangem.domain.visa.model.VisaCardId
import com.tangem.domain.visa.repository.VisaActivationRepository
import com.tangem.domain.visa.repository.VisaAuthRepository
import com.tangem.domain.wallets.builder.ColdUserWalletBuilder
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
import com.tangem.features.onboarding.v2.visa.impl.child.inprogress.OnboardingVisaInProgressComponent.Config
import com.tangem.features.onboarding.v2.visa.impl.child.inprogress.OnboardingVisaInProgressComponent.Params
import com.tangem.features.onboarding.v2.visa.impl.child.welcome.model.analytics.OnboardingVisaAnalyticsEvent
@ -46,7 +46,7 @@ internal class OnboardingVisaInProgressModel @Inject constructor(
private val visaAuthTokenStorage: VisaAuthTokenStorage,
private val otpStorage: VisaOTPStorage,
private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory,
private val userWalletsListManager: UserWalletsListManager,
private val saveWalletUseCase: SaveWalletUseCase,
private val uiMessageSender: UiMessageSender,
private val analyticsEventHandler: AnalyticsEventHandler,
) : Model() {
@ -173,7 +173,7 @@ internal class OnboardingVisaInProgressModel @Inject constructor(
}
val userWallet = createUserWallet(params.scanResponse, newTokens)
userWalletsListManager.save(userWallet)
saveWalletUseCase(userWallet)
visaAuthTokenStorage.remove(params.scanResponse.card.cardId)
otpStorage.removeOTP(params.scanResponse.card.cardId)

View file

@ -25,12 +25,12 @@ import com.tangem.datasource.api.express.models.response.TxDetails
import com.tangem.datasource.crypto.DataSignatureVerifier
import com.tangem.datasource.exchangeservice.swap.ExpressUtils
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.transaction.models.AssetRequirementsCondition
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.transaction.models.AssetRequirementsCondition
import com.tangem.feature.swap.converters.*
import com.tangem.feature.swap.domain.api.SwapRepository
import com.tangem.feature.swap.domain.models.ExpressDataError
@ -51,7 +51,7 @@ internal class DefaultSwapRepository(
private val tangemExpressApi: TangemExpressApi,
private val coroutineDispatcher: CoroutineDispatcherProvider,
private val walletManagersFacade: WalletManagersFacade,
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsStore: UserWalletsStore,
private val errorsDataConverter: ErrorsDataConverter,
private val dataSignatureVerifier: DataSignatureVerifier,
private val appPreferencesStore: AppPreferencesStore,
@ -409,7 +409,7 @@ internal class DefaultSwapRepository(
cryptoCurrencyFactory.createCoin(
blockchain = blockchain,
extraDerivationPath = null,
userWallet = requireNotNull(userWalletsListManager.selectedUserWalletSync),
userWallet = requireNotNull(userWalletsStore.selectedUserWalletOrNull),
),
)
}

View file

@ -8,8 +8,8 @@ import com.tangem.datasource.api.express.models.response.ExpressErrorResponse
import com.tangem.datasource.crypto.DataSignatureVerifier
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.feature.swap.DefaultSwapRepository
import com.tangem.feature.swap.DefaultSwapTransactionRepository
import com.tangem.feature.swap.converters.ErrorsDataConverter
@ -33,7 +33,7 @@ internal class SwapDataModule {
coroutineDispatcher: CoroutineDispatcherProvider,
dataSignature: DataSignatureVerifier,
walletManagerFacade: WalletManagersFacade,
userWalletsListManager: UserWalletsListManager,
userWalletsStore: UserWalletsStore,
errorsDataConverter: ErrorsDataConverter,
@NetworkMoshi moshi: Moshi,
excludedBlockchains: ExcludedBlockchains,
@ -43,7 +43,7 @@ internal class SwapDataModule {
tangemExpressApi = tangemExpressApi,
coroutineDispatcher = coroutineDispatcher,
walletManagersFacade = walletManagerFacade,
userWalletsListManager = userWalletsListManager,
userWalletsStore = userWalletsStore,
errorsDataConverter = errorsDataConverter,
dataSignatureVerifier = dataSignature,
moshi = moshi,

View file

@ -2,29 +2,44 @@ package com.tangem.feature.wallet.presentation.wallet.domain
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.models.wallet.copy
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository
import timber.log.Timber
class WalletNameMigrationUseCase(
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val useNewListRepository: Boolean,
private val walletNamesMigrationRepository: WalletNamesMigrationRepository,
) {
suspend operator fun invoke() {
val wallets = userWalletsListManager.userWalletsSync
if (walletNamesMigrationRepository.isMigrationDone()) {
return
}
val existingNames: MutableSet<String> = mutableSetOf()
wallets.indices.forEach { i ->
val defaultName = wallets[i].name
val suggestedWalletName = suggestedWalletName(defaultName, existingNames)
if (defaultName != suggestedWalletName) {
userWalletsListManager.update(wallets[i].walletId) { it.copy(name = suggestedWalletName) }
if (useNewListRepository) {
val wallets = userWalletsListRepository.userWalletsSync()
val existingNames: MutableSet<String> = mutableSetOf()
wallets.forEach {
val defaultName = it.name
val suggestedWalletName = suggestedWalletName(defaultName, existingNames)
if (defaultName != suggestedWalletName) {
userWalletsListRepository.saveWithoutLock(it.copy(name = suggestedWalletName), canOverride = true)
}
Timber.tag("Migrated names").e(it.walletId.toString() + " " + suggestedWalletName)
}
} else {
val wallets = userWalletsListManager.userWalletsSync
val existingNames: MutableSet<String> = mutableSetOf()
wallets.indices.forEach { i ->
val defaultName = wallets[i].name
val suggestedWalletName = suggestedWalletName(defaultName, existingNames)
if (defaultName != suggestedWalletName) {
userWalletsListManager.update(wallets[i].walletId) { it.copy(name = suggestedWalletName) }
}
Timber.tag("Migrated names").e(i.toString() + " " + suggestedWalletName)
}
Timber.tag("Migrated names").e(i.toString() + " " + suggestedWalletName)
}
walletNamesMigrationRepository.setMigrationDone()