Updated on 2026-08-14

This commit is contained in:
Tangem 2025-09-05 13:19:12 +03:00
parent 411a591509
commit 94a4486443
28 changed files with 393 additions and 178 deletions

View file

@ -10,6 +10,7 @@ import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.core.wallets.UserWalletsListRepository
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isLocked
@ -35,6 +36,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.transformers.*
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
import com.tangem.features.biometry.AskBiometryComponent
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.features.pushnotifications.api.PushNotificationsModelCallbacks
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
import com.tangem.features.wallet.deeplink.WalletDeepLinkActionListener
@ -58,7 +60,7 @@ internal class WalletModel @Inject constructor(
private val walletScreenContentLoader: WalletScreenContentLoader,
private val getSelectedWalletUseCase: GetSelectedWalletUseCase,
private val getWalletsUseCase: GetWalletsUseCase,
private val shouldShowSaveWalletScreenUseCase: ShouldShowSaveWalletScreenUseCase,
private val shouldShowAskBiometryUseCase: ShouldShowAskBiometryUseCase,
private val shouldShowMarketsTooltipUseCase: ShouldShowMarketsTooltipUseCase,
private val setWalletFirstTimeUsageUseCase: SetWalletFirstTimeUsageUseCase,
private val canUseBiometryUseCase: CanUseBiometryUseCase,
@ -80,6 +82,8 @@ internal class WalletModel @Inject constructor(
private val setNotificationsEnabledUseCase: SetNotificationsEnabledUseCase,
private val shouldSaveUserWalletsSyncUseCase: ShouldSaveUserWalletsSyncUseCase,
private val getIsHuaweiDeviceWithoutGoogleServicesUseCase: GetIsHuaweiDeviceWithoutGoogleServicesUseCase,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
private val userWalletsListRepository: UserWalletsListRepository,
val screenLifecycleProvider: ScreenLifecycleProvider,
val innerWalletRouter: InnerWalletRouter,
) : Model() {
@ -137,7 +141,7 @@ internal class WalletModel @Inject constructor(
modelScope.launch(dispatchers.main) {
withContext(dispatchers.io) { delay(timeMillis = 1_800) }
if (isShowSaveWalletScreenEnabled()) {
if (shouldShowAskBiometryBottomSheet()) {
innerWalletRouter.dialogNavigation.activate(
configuration = WalletDialogConfig.AskForBiometry,
)
@ -159,8 +163,15 @@ internal class WalletModel @Inject constructor(
}
}
private suspend fun isShowSaveWalletScreenEnabled(): Boolean {
return innerWalletRouter.isWalletLastScreen() && shouldShowSaveWalletScreenUseCase() && canUseBiometryUseCase()
private suspend fun shouldShowAskBiometryBottomSheet(): Boolean {
return if (hotWalletFeatureToggles.isHotWalletEnabled) {
userWalletsListRepository.userWalletsSync().any { it is UserWallet.Cold } &&
innerWalletRouter.isWalletLastScreen() &&
shouldShowAskBiometryUseCase() &&
canUseBiometryUseCase()
} else {
innerWalletRouter.isWalletLastScreen() && shouldShowAskBiometryUseCase() && canUseBiometryUseCase()
}
}
private fun subscribeToUserWalletsUpdates() = channelFlow<Unit> {

View file

@ -76,18 +76,19 @@ internal class WalletsUpdateActionResolver @Inject constructor(
isAnyWalletNameChanged(state, wallets) -> {
getRenameWalletsAction(state, wallets)
}
isAnyHotWalletBackedUp(state, wallets) -> {
isAnyHotWalletBackedUpChange(state, wallets) -> {
getHotWalletsBackedUpAction(state, wallets)
}
else -> getUpdateSelectedWalletAction(state, wallets, selectedWallet)
}
}
private fun isAnyHotWalletBackedUp(state: WalletScreenState, wallets: List<UserWallet>): Boolean {
private fun isAnyHotWalletBackedUpChange(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()
val walletsToUpdate = wallets.filter {
it is UserWallet.Hot && it.backedUp == incompleteActivationWalletIds.contains(it.walletId)
}
return walletsToUpdate.isNotEmpty()
}
private fun isWalletsCountChanged(state: WalletScreenState, wallets: List<UserWallet>): Boolean {
@ -163,8 +164,7 @@ internal class WalletsUpdateActionResolver @Inject constructor(
val incompleteActivationWalletIds = state.incompleteActivationWalletIds()
val walletsToUpdate = wallets.filter {
it is UserWallet.Hot && it.backedUp &&
incompleteActivationWalletIds.contains(it.walletId)
it is UserWallet.Hot && it.backedUp == incompleteActivationWalletIds.contains(it.walletId)
}
return Action.ReloadWarningsForWallets(walletsToUpdate)

View file

@ -13,6 +13,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.onEach
internal class MultiWalletWarningsSubscriber(
@ -31,6 +32,11 @@ internal class MultiWalletWarningsSubscriber(
.onEach { warnings ->
val displayedState = stateHolder.getWalletState(userWallet.walletId)
// Wait until the wallet appears in the list
stateHolder.uiState.first {
it.wallets.any { walletState -> walletState.walletCardState.id == userWallet.walletId }
}
stateHolder.update(SetWarningsTransformer(userWallet.walletId, warnings))
walletWarningsAnalyticsSender.send(displayedState, warnings)
walletWarningsSingleEventSender.send(

View file

@ -43,7 +43,7 @@ internal class DefaultUserWalletsFetcher @AssistedInject constructor(
@Assisted private val onWalletClick: (UserWalletId) -> Unit,
@Assisted private val messageSender: UiMessageSender,
@Assisted("onlyMultiCurrency") private val onlyMultiCurrency: Boolean,
@Assisted("authMode") private val authMode: Boolean,
@Assisted("isAuthMode") private val isAuthMode: Boolean,
private val userWalletImageFetcher: UserWalletImageFetcher,
dispatchers: CoroutineDispatcherProvider,
) : UserWalletsFetcher {
@ -55,7 +55,7 @@ internal class DefaultUserWalletsFetcher @AssistedInject constructor(
override val userWallets: Flow<ImmutableList<UserWalletItemUM>> = walletsFlow.transformLatest { wallets ->
val uiModels = UserWalletItemUMConverter(
onClick = onWalletClick,
authMode = authMode,
isAuthMode = isAuthMode,
).convertList(wallets)
.toImmutableList()
@ -64,7 +64,7 @@ internal class DefaultUserWalletsFetcher @AssistedInject constructor(
combine(
flow = getSelectedAppCurrencyUseCase().distinctUntilChanged(),
flow2 = getBalanceHidingSettingsUseCase().distinctUntilChanged(),
flow3 = getWalletTotalBalanceUseCase(wallets.map(UserWallet::walletId)).distinctUntilChanged(),
flow3 = getTotalBalanceFlow(wallets),
flow4 = userWalletImageFetcher.walletsImage(wallets, ArtworkSize.SMALL),
) { maybeAppCurrency, balanceHidingSettings, maybeBalances, artworks ->
createUiModels(
@ -88,6 +88,19 @@ internal class DefaultUserWalletsFetcher @AssistedInject constructor(
}
.flowOn(dispatchers.default)
private fun getTotalBalanceFlow(
wallets: List<UserWallet>,
): Flow<Lce<TokenListError, Map<UserWalletId, TotalFiatBalance>>> {
val walletIds = wallets.map(UserWallet::walletId)
return if (isAuthMode) {
// We should not load balances in auth mode
flowOf(Lce.Loading(walletIds.associateWith { TotalFiatBalance.Loading }))
} else {
getWalletTotalBalanceUseCase(walletIds).distinctUntilChanged()
}
}
private fun createUiModels(
wallets: List<UserWallet>,
maybeAppCurrency: Either<SelectedAppCurrencyError, AppCurrency>,
@ -117,7 +130,7 @@ internal class DefaultUserWalletsFetcher @AssistedInject constructor(
balance = balance,
isBalanceHidden = balanceHidingSettings.isBalanceHidden,
artwork = artworks[userWallet.walletId],
authMode = authMode,
isAuthMode = isAuthMode,
)
.convert(userWallet)
}
@ -136,7 +149,7 @@ internal class DefaultUserWalletsFetcher @AssistedInject constructor(
override fun create(
messageSender: UiMessageSender,
@Assisted("onlyMultiCurrency") onlyMultiCurrency: Boolean,
@Assisted("authMode") authMode: Boolean,
@Assisted("isAuthMode") isAuthMode: Boolean,
onWalletClick: (UserWalletId) -> Unit,
): DefaultUserWalletsFetcher
}