Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-17 14:39:32 +04:00
parent 6b75b48f1e
commit 8e6eca1b4c
23 changed files with 327 additions and 216 deletions

View file

@ -0,0 +1,61 @@
package com.tangem.tap.domain
import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.common.wallets.UserWalletSelectedHandler
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.sdk.api.TangemSdkManager
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.saveInAndJoin
import kotlinx.coroutines.launch
import javax.inject.Inject
import javax.inject.Singleton
/**
* Default implementation of [UserWalletSelectedHandler].
*
* Runs three side effects on every selection: updates the analytics tracking context, updates the
* Tangem SDK displayed card-id numbers count (cold wallets only), and recomputes the access code
* request policy (cold wallets only). Hot wallets trigger only the tracking-context update.
*
* Invocations are serialised via [JobHolder]: if a new [invoke] arrives while the previous one is
* still running, the previous load is cancelled and the new one replaces it. The method suspends
* until the newly launched load completes.
*/
@Singleton
internal class DefaultUserWalletSelectedHandler @Inject constructor(
private val trackingContextProxy: TrackingContextProxy,
private val tangemSdkManager: TangemSdkManager,
private val settingsRepository: SettingsRepository,
private val cardSdkConfigRepository: CardSdkConfigRepository,
private val appCoroutineScope: AppCoroutineScope,
) : UserWalletSelectedHandler {
private val loadUserWalletDataJob: JobHolder = JobHolder()
override suspend fun invoke(userWallet: UserWallet) {
appCoroutineScope.launch { loadUserWalletData(userWallet) }
.saveInAndJoin(loadUserWalletDataJob)
}
private suspend fun loadUserWalletData(userWallet: UserWallet) {
trackingContextProxy.setContext(userWallet)
if (userWallet is UserWallet.Cold) {
val scanResponse = userWallet.scanResponse
tangemSdkManager.changeDisplayedCardIdNumbersCount(scanResponse)
updateAccessCodeRequestPolicy(scanResponse)
}
}
private suspend fun updateAccessCodeRequestPolicy(scanResponse: ScanResponse) {
val shouldSaveAccessCodes = settingsRepository.shouldSaveAccessCodes()
cardSdkConfigRepository.setAccessCodeRequestPolicy(
isBiometricsRequestPolicy = shouldSaveAccessCodes && scanResponse.card.isAccessCodeSet,
)
}
}

View file

@ -9,6 +9,7 @@ import com.tangem.common.services.secure.SecureStorage
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.domain.common.wallets.UserWalletSelectedHandler
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.hotwallet.repository.HotWalletRepository
import com.tangem.domain.models.scan.serialization.*
@ -32,6 +33,7 @@ import com.tangem.tap.tangemSdkManager
import com.tangem.utils.Provider
import com.tangem.utils.ProviderSuspend
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Lazy
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -56,6 +58,7 @@ internal object UserWalletsListRepositoryModule {
analyticsEventHandler: AnalyticsEventHandler,
hotWalletRepository: HotWalletRepository,
mobileWalletPromoRepository: MobileWalletPromoRepository,
userWalletSelectedHandler: Lazy<UserWalletSelectedHandler>,
): UserWalletsListRepository {
val moshi = buildMoshi()
val secureStorage = buildSecureStorage(applicationContext = applicationContext)
@ -107,6 +110,7 @@ internal object UserWalletsListRepositoryModule {
analyticsEventHandler = analyticsEventHandler,
hotWalletRepository = hotWalletRepository,
mobileWalletPromoRepository = mobileWalletPromoRepository,
userWalletSelectedHandler = userWalletSelectedHandler,
)
}

View file

@ -13,6 +13,7 @@ import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
import com.tangem.domain.common.wallets.UserWalletSelectedHandler
import com.tangem.domain.common.wallets.UserWalletTransformAction
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.common.wallets.UserWalletsListRepository.LockMethod
@ -35,6 +36,7 @@ import com.tangem.tap.domain.userWalletList.utils.*
import com.tangem.utils.Provider
import com.tangem.utils.ProviderSuspend
import com.tangem.utils.coroutines.runSuspendCatching
import dagger.Lazy
import com.tangem.utils.extensions.addOrReplace
import com.tangem.utils.extensions.indexOfFirstOrNull
import kotlinx.coroutines.NonCancellable
@ -60,6 +62,7 @@ internal class DefaultUserWalletsListRepository(
private val analyticsEventHandler: AnalyticsEventHandler,
private val hotWalletRepository: HotWalletRepository,
private val mobileWalletPromoRepository: MobileWalletPromoRepository,
private val userWalletSelectedHandler: Lazy<UserWalletSelectedHandler>,
) : UserWalletsListRepository {
override val userWallets = MutableStateFlow<List<UserWallet>?>(null)
@ -88,15 +91,13 @@ internal class DefaultUserWalletsListRepository(
.map { wallets.updateWith(it) }
}
.doOnSuccess { loadedWallets ->
userWallets.update { _ ->
val selectedUserWalletId = selectedUserWalletRepository.get()
selectedUserWallet.value = loadedWallets.firstOrNull { it.walletId == selectedUserWalletId }
?: loadedWallets.firstOrNull()?.also {
selectedUserWalletRepository.set(it.walletId)
}
loadedWallets
}
val selectedUserWalletId = selectedUserWalletRepository.get()
val initialSelection = loadedWallets.firstOrNull { it.walletId == selectedUserWalletId }
?: loadedWallets.firstOrNull()?.also {
selectedUserWalletRepository.set(it.walletId)
}
userWallets.value = loadedWallets
setSelectedUserWallet(initialSelection)
}
}
}
@ -117,7 +118,7 @@ internal class DefaultUserWalletsListRepository(
val userWallet = userWallets.value?.find { it.walletId == userWalletId }
?: raise(SelectWalletError.UnableToSelectUserWallet)
selectedUserWalletRepository.set(userWalletId)
selectedUserWallet.value = userWallet
setSelectedUserWallet(userWallet)
userWallet
}
@ -160,7 +161,7 @@ internal class DefaultUserWalletsListRepository(
// update the selectedUserWallet state if it is the only wallet
if (userWallets.value?.size == 1) {
selectedUserWalletRepository.set(userWallet.walletId)
selectedUserWallet.value = userWallet
setSelectedUserWallet(userWallet)
}
userWallet
@ -223,20 +224,24 @@ internal class DefaultUserWalletsListRepository(
removeHotWalletsFromSDKAndRepos(userWalletIds)
userWallets.update { currentWallets ->
val updatedWallets = currentWallets?.filter { userWalletIds.contains(it.walletId).not() }
selectedUserWallet.update { currentSelected ->
if (currentSelected == null) return@update null
val newSelected = updatedWallets?.findAvailableUserWallet(
currentWallets.indexOfFirstOrNull { it.walletId == currentSelected.walletId } ?: 0,
)
if (newSelected == null) {
onAllWalletsDeleted()
}
selectedUserWalletRepository.set(newSelected?.walletId)
newSelected
val currentWallets = userWallets.value
val currentSelected = selectedUserWallet.value
val updatedWallets = currentWallets?.filter { userWalletIds.contains(it.walletId).not() }
val newSelected = if (currentSelected == null) {
null
} else {
updatedWallets?.findAvailableUserWallet(
currentWallets.indexOfFirstOrNull { it.walletId == currentSelected.walletId } ?: 0,
)
}
userWallets.value = updatedWallets
if (currentSelected != null) {
if (newSelected == null) {
onAllWalletsDeleted()
}
updatedWallets
selectedUserWalletRepository.set(newSelected?.walletId)
setSelectedUserWallet(newSelected)
}
}
@ -523,6 +528,18 @@ internal class DefaultUserWalletsListRepository(
return tangemSdkManagerProvider.invoke().canUseBiometry && isBiometricAuthenticationUsed
}
/**
* Writes [userWallet] into [selectedUserWallet] and invokes [userWalletSelectedHandler] when
* the selected [UserWalletId] actually changes. Same-id refreshes stay silent.
*/
private suspend fun setSelectedUserWallet(userWallet: UserWallet?) {
val previousId = selectedUserWallet.value?.walletId
selectedUserWallet.value = userWallet
if (userWallet != null && previousId != userWallet.walletId) {
userWalletSelectedHandler.get().invoke(userWallet)
}
}
private fun updateWallets(block: (List<UserWallet>?) -> List<UserWallet>?) {
userWallets.update { wallets ->
val updated = block(wallets)