Updated on 2026-08-14
This commit is contained in:
parent
f466395589
commit
8cff4c1bba
5 changed files with 97 additions and 5 deletions
|
|
@ -62,7 +62,7 @@ internal class DefaultUserWalletsListRepository(
|
||||||
// If we don't save persistent information, we don't need to load user wallets
|
// If we don't save persistent information, we don't need to load user wallets
|
||||||
// and we should clear any existing data
|
// and we should clear any existing data
|
||||||
clearPersistentData()
|
clearPersistentData()
|
||||||
userWallets.value = emptyList()
|
updateWallets { emptyList() }
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,7 +118,7 @@ internal class DefaultUserWalletsListRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the userWallets state and add if it doesn't exist
|
// update the userWallets state and add if it doesn't exist
|
||||||
userWallets.update { currentWallets ->
|
updateWallets { currentWallets ->
|
||||||
val wallets = currentWallets ?: emptyList()
|
val wallets = currentWallets ?: emptyList()
|
||||||
if (wallets.any { it.walletId == userWallet.walletId }) {
|
if (wallets.any { it.walletId == userWallet.walletId }) {
|
||||||
wallets.map { if (it.walletId == userWallet.walletId) userWallet else it }
|
wallets.map { if (it.walletId == userWallet.walletId) userWallet else it }
|
||||||
|
|
@ -248,7 +248,7 @@ internal class DefaultUserWalletsListRepository(
|
||||||
removePasswordAttempts(userWallet)
|
removePasswordAttempts(userWallet)
|
||||||
|
|
||||||
sensitiveInformationRepository.getAll(listOf(encryptionKey))
|
sensitiveInformationRepository.getAll(listOf(encryptionKey))
|
||||||
.doOnSuccess { sensitiveInfo -> userWallets.update { it?.updateWith(sensitiveInfo) } }
|
.doOnSuccess { sensitiveInfo -> updateWallets { it?.updateWith(sensitiveInfo) } }
|
||||||
.doOnFailure { error ->
|
.doOnFailure { error ->
|
||||||
raise(UnlockWalletError.UnableToUnlock)
|
raise(UnlockWalletError.UnableToUnlock)
|
||||||
}
|
}
|
||||||
|
|
@ -306,7 +306,7 @@ internal class DefaultUserWalletsListRepository(
|
||||||
|
|
||||||
sensitiveInformationRepository.getAll(allKeys)
|
sensitiveInformationRepository.getAll(allKeys)
|
||||||
.doOnSuccess { sensitiveInfo ->
|
.doOnSuccess { sensitiveInfo ->
|
||||||
userWallets.update { it?.updateWith(sensitiveInfo) }
|
updateWallets { it?.updateWith(sensitiveInfo) }
|
||||||
}
|
}
|
||||||
.doOnFailure { raise(UnlockWalletError.UnableToUnlock) }
|
.doOnFailure { raise(UnlockWalletError.UnableToUnlock) }
|
||||||
}
|
}
|
||||||
|
|
@ -318,7 +318,7 @@ internal class DefaultUserWalletsListRepository(
|
||||||
raise(LockWalletsError.NothingToLock)
|
raise(LockWalletsError.NothingToLock)
|
||||||
}
|
}
|
||||||
|
|
||||||
userWallets.update {
|
updateWallets {
|
||||||
it?.map {
|
it?.map {
|
||||||
if (it.walletId !in unsecuredWalletIds) {
|
if (it.walletId !in unsecuredWalletIds) {
|
||||||
it.lock()
|
it.lock()
|
||||||
|
|
@ -389,6 +389,15 @@ internal class DefaultUserWalletsListRepository(
|
||||||
return tangemSdkManagerProvider.invoke().canUseBiometry && useBiometricAuthentication
|
return tangemSdkManagerProvider.invoke().canUseBiometry && useBiometricAuthentication
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateWallets(block: (List<UserWallet>?) -> List<UserWallet>?) {
|
||||||
|
userWallets.update(block)
|
||||||
|
|
||||||
|
selectedUserWallet.update { currentSelected ->
|
||||||
|
if (currentSelected == null) return@update null
|
||||||
|
userWallets.value?.find { it.walletId == currentSelected.walletId }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the nearest available wallet that can be selected
|
* Find the nearest available wallet that can be selected
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -363,6 +363,9 @@ internal class WalletModel @Inject constructor(
|
||||||
is WalletsUpdateActionResolver.Action.RenameWallets -> {
|
is WalletsUpdateActionResolver.Action.RenameWallets -> {
|
||||||
stateHolder.update(transformer = RenameWalletsTransformer(renamedWallets = action.renamedWallets))
|
stateHolder.update(transformer = RenameWalletsTransformer(renamedWallets = action.renamedWallets))
|
||||||
}
|
}
|
||||||
|
is WalletsUpdateActionResolver.Action.ReloadWarningsForWallets -> {
|
||||||
|
reloadWarnings(action)
|
||||||
|
}
|
||||||
WalletsUpdateActionResolver.Action.EmptyWallets -> {
|
WalletsUpdateActionResolver.Action.EmptyWallets -> {
|
||||||
Timber.w("Wallets list is empty!")
|
Timber.w("Wallets list is empty!")
|
||||||
}
|
}
|
||||||
|
|
@ -372,6 +375,17 @@ internal class WalletModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun reloadWarnings(action: WalletsUpdateActionResolver.Action.ReloadWarningsForWallets) {
|
||||||
|
action.wallets.forEach {
|
||||||
|
walletScreenContentLoader.load(
|
||||||
|
userWallet = it,
|
||||||
|
clickIntents = clickIntents,
|
||||||
|
coroutineScope = modelScope,
|
||||||
|
isRefresh = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun initializeWallets(action: WalletsUpdateActionResolver.Action.InitializeWallets) {
|
private suspend fun initializeWallets(action: WalletsUpdateActionResolver.Action.InitializeWallets) {
|
||||||
stateHolder.update(
|
stateHolder.update(
|
||||||
transformer = InitializeWalletsTransformer(
|
transformer = InitializeWalletsTransformer(
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import com.tangem.domain.models.wallet.isLocked
|
||||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||||
import com.tangem.feature.wallet.presentation.wallet.state.model.NOT_INITIALIZED_WALLET_INDEX
|
import com.tangem.feature.wallet.presentation.wallet.state.model.NOT_INITIALIZED_WALLET_INDEX
|
||||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
|
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
|
||||||
|
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
||||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
@ -75,10 +76,20 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
||||||
isAnyWalletNameChanged(state, wallets) -> {
|
isAnyWalletNameChanged(state, wallets) -> {
|
||||||
getRenameWalletsAction(state, wallets)
|
getRenameWalletsAction(state, wallets)
|
||||||
}
|
}
|
||||||
|
isAnyHotWalletBackedUp(state, wallets) -> {
|
||||||
|
getHotWalletsBackedUpAction(state, wallets)
|
||||||
|
}
|
||||||
else -> getUpdateSelectedWalletAction(state, wallets, selectedWallet)
|
else -> getUpdateSelectedWalletAction(state, wallets, selectedWallet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isAnyHotWalletBackedUp(state: WalletScreenState, wallets: List<UserWallet>): Boolean {
|
||||||
|
val incompleteActivationWalletIds = state.incompleteActivationWalletIds()
|
||||||
|
return incompleteActivationWalletIds.mapNotNull { walletId ->
|
||||||
|
wallets.firstOrNull { it.walletId == walletId && it is UserWallet.Hot && it.backedUp }
|
||||||
|
}.isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
private fun isWalletsCountChanged(state: WalletScreenState, wallets: List<UserWallet>): Boolean {
|
private fun isWalletsCountChanged(state: WalletScreenState, wallets: List<UserWallet>): Boolean {
|
||||||
val prevWalletsSize = state.wallets.size
|
val prevWalletsSize = state.wallets.size
|
||||||
val walletsSize = wallets.size
|
val walletsSize = wallets.size
|
||||||
|
|
@ -145,6 +156,20 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
||||||
return prevWalletsIds == newWalletsIds && isAnyNameChanged
|
return prevWalletsIds == newWalletsIds && isAnyNameChanged
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getHotWalletsBackedUpAction(
|
||||||
|
state: WalletScreenState,
|
||||||
|
wallets: List<UserWallet>,
|
||||||
|
): Action.ReloadWarningsForWallets {
|
||||||
|
val incompleteActivationWalletIds = state.incompleteActivationWalletIds()
|
||||||
|
|
||||||
|
val walletsToUpdate = wallets.filter {
|
||||||
|
it is UserWallet.Hot && it.backedUp &&
|
||||||
|
incompleteActivationWalletIds.contains(it.walletId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Action.ReloadWarningsForWallets(walletsToUpdate)
|
||||||
|
}
|
||||||
|
|
||||||
private fun getRenameWalletsAction(state: WalletScreenState, wallets: List<UserWallet>): Action.RenameWallets {
|
private fun getRenameWalletsAction(state: WalletScreenState, wallets: List<UserWallet>): Action.RenameWallets {
|
||||||
val prevWallets = state.wallets.map { it.walletCardState.id to it.walletCardState.title }
|
val prevWallets = state.wallets.map { it.walletCardState.id to it.walletCardState.title }
|
||||||
val newWallets = wallets.map { it.walletId to it.name }
|
val newWallets = wallets.map { it.walletId to it.name }
|
||||||
|
|
@ -199,6 +224,16 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
||||||
?: error("Previous selected wallet is not found")
|
?: error("Previous selected wallet is not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun WalletScreenState.incompleteActivationWalletIds(): List<UserWalletId> {
|
||||||
|
return wallets.mapNotNull {
|
||||||
|
if (it.warnings.any { it is WalletNotification.FinishWalletActivation }) {
|
||||||
|
it.walletCardState.id
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun List<UserWallet>.indexOfWallet(id: UserWalletId): Int {
|
private fun List<UserWallet>.indexOfWallet(id: UserWalletId): Int {
|
||||||
val selectedIndex = indexOfFirst { it.walletId == id }
|
val selectedIndex = indexOfFirst { it.walletId == id }
|
||||||
|
|
||||||
|
|
@ -323,6 +358,10 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class ReloadWarningsForWallets(
|
||||||
|
val wallets: List<UserWallet>,
|
||||||
|
) : Action()
|
||||||
|
|
||||||
data object EmptyWallets : Action()
|
data object EmptyWallets : Action()
|
||||||
|
|
||||||
data object Unknown : Action()
|
data object Unknown : Action()
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBot
|
||||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.OpenBottomSheetTransformer
|
import com.tangem.feature.wallet.presentation.wallet.state.transformers.OpenBottomSheetTransformer
|
||||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.MultiWalletCurrencyActionsConverter
|
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.MultiWalletCurrencyActionsConverter
|
||||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
|
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
|
||||||
|
import com.tangem.features.hotwallet.HotWalletFeatureToggles
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.take
|
import kotlinx.coroutines.flow.take
|
||||||
|
|
@ -76,9 +77,16 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
||||||
private val reduxStateHolder: ReduxStateHolder,
|
private val reduxStateHolder: ReduxStateHolder,
|
||||||
private val walletEventSender: WalletEventSender,
|
private val walletEventSender: WalletEventSender,
|
||||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||||
|
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
|
||||||
) : BaseWalletClickIntents(), WalletContentClickIntents {
|
) : BaseWalletClickIntents(), WalletContentClickIntents {
|
||||||
|
|
||||||
override fun onDetailsClick() {
|
override fun onDetailsClick() {
|
||||||
|
if (hotWalletFeatureToggles.isHotWalletEnabled) {
|
||||||
|
router.openDetailsScreen(stateHolder.getSelectedWalletId())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will be removed after Hot Wallet release
|
||||||
modelScope.launch(dispatchers.main) {
|
modelScope.launch(dispatchers.main) {
|
||||||
val userWalletId = stateHolder.getSelectedWalletId()
|
val userWalletId = stateHolder.getSelectedWalletId()
|
||||||
val userWallet = getUserWalletUseCase(userWalletId).getOrElse {
|
val userWallet = getUserWalletUseCase(userWalletId).getOrElse {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import com.tangem.core.ui.extensions.resourceReference
|
||||||
import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase
|
import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase
|
||||||
import com.tangem.domain.card.SetCardWasScannedUseCase
|
import com.tangem.domain.card.SetCardWasScannedUseCase
|
||||||
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
|
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
|
||||||
|
import com.tangem.domain.core.wallets.UserWalletsListRepository
|
||||||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
|
|
@ -44,6 +45,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletBottomShe
|
||||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent
|
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent
|
||||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBottomSheetTransformer
|
import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBottomSheetTransformer
|
||||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
|
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
|
||||||
|
import com.tangem.features.hotwallet.HotWalletFeatureToggles
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
|
|
@ -117,6 +119,8 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
||||||
private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
|
private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
|
||||||
private val stakingIdFactory: StakingIdFactory,
|
private val stakingIdFactory: StakingIdFactory,
|
||||||
private val appRouter: AppRouter,
|
private val appRouter: AppRouter,
|
||||||
|
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
|
||||||
|
private val userWalletsListRepository: UserWalletsListRepository,
|
||||||
) : BaseWalletClickIntents(), WalletWarningsClickIntents {
|
) : BaseWalletClickIntents(), WalletWarningsClickIntents {
|
||||||
|
|
||||||
override fun onAddBackupCardClick() {
|
override fun onAddBackupCardClick() {
|
||||||
|
|
@ -168,6 +172,22 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
||||||
override fun onOpenUnlockWalletsBottomSheetClick() {
|
override fun onOpenUnlockWalletsBottomSheetClick() {
|
||||||
analyticsEventHandler.send(MainScreen.WalletUnlockTapped)
|
analyticsEventHandler.send(MainScreen.WalletUnlockTapped)
|
||||||
|
|
||||||
|
if (hotWalletFeatureToggles.isHotWalletEnabled) {
|
||||||
|
modelScope.launch {
|
||||||
|
userWalletsListRepository.unlockAllWallets()
|
||||||
|
.onLeft {
|
||||||
|
val selectedUserWallet = getSelectedUserWallet() ?: return@onLeft
|
||||||
|
val method = when (selectedUserWallet) {
|
||||||
|
is UserWallet.Cold -> UserWalletsListRepository.UnlockMethod.Scan
|
||||||
|
is UserWallet.Hot -> UserWalletsListRepository.UnlockMethod.AccessCode
|
||||||
|
}
|
||||||
|
userWalletsListRepository.unlock(stateHolder.getSelectedWalletId(), method)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will be removed after hot wallet release
|
||||||
stateHolder.showBottomSheet(
|
stateHolder.showBottomSheet(
|
||||||
WalletBottomSheetConfig.UnlockWallets(
|
WalletBottomSheetConfig.UnlockWallets(
|
||||||
onUnlockClick = this::onUnlockWalletClick,
|
onUnlockClick = this::onUnlockWalletClick,
|
||||||
|
|
@ -176,6 +196,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated("Will be removed with hot wallet release")
|
||||||
override fun onUnlockWalletClick() {
|
override fun onUnlockWalletClick() {
|
||||||
analyticsEventHandler.send(MainScreen.UnlockAllWithBiometrics)
|
analyticsEventHandler.send(MainScreen.UnlockAllWithBiometrics)
|
||||||
|
|
||||||
|
|
@ -203,6 +224,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
||||||
walletEventSender.send(event)
|
walletEventSender.send(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated("Will be removed with hot wallet release")
|
||||||
override fun onScanToUnlockWalletClick() {
|
override fun onScanToUnlockWalletClick() {
|
||||||
analyticsEventHandler.send(MainScreen.UnlockWithCardScan)
|
analyticsEventHandler.send(MainScreen.UnlockWithCardScan)
|
||||||
openScanCardDialog()
|
openScanCardDialog()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue