Updated on 2026-08-14

This commit is contained in:
Tangem 2023-10-02 12:38:55 +03:00
parent b91efa6730
commit d36d3f5427
13 changed files with 173 additions and 18 deletions

View file

@ -5,6 +5,8 @@ import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.demo.DemoConfig
import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.wallets.legacy.WalletsStateHolder
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
import com.tangem.tap.domain.TangemSdkManager
import com.tangem.tap.domain.card.DefaultDerivePublicKeysUseCase
import dagger.Module
@ -62,4 +64,10 @@ internal object CardDomainModule {
fun provideDerivePublicKeysUseCase(tangemSdkManager: TangemSdkManager): DerivePublicKeysUseCase {
return DefaultDerivePublicKeysUseCase(tangemSdkManager = tangemSdkManager)
}
@Provides
@ViewModelScoped
fun provideIsNeedToBackupUseCase(walletStateHolder: WalletsStateHolder): IsNeedToBackupUseCase {
return IsNeedToBackupUseCase(walletStateHolder)
}
}

View file

@ -450,6 +450,23 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
Analytics.send(Onboarding.Backup.Finished(backupState.backupCardsNumber))
}
userWalletsListManager.selectedUserWalletSync?.walletId?.let {
scope.launch {
userWalletsListManager.update(
userWalletId = it,
update = { wallet ->
wallet.copy(
scanResponse = updateScanResponseAfterBackup(
scanResponse = wallet.scanResponse,
backupState = backupState,
),
)
},
)
store.dispatchOnMain(GlobalAction.UpdateUserWalletsListManager(userWalletsListManager))
}
}
val notActivatedCardIds = gatherCardIds(backupState, card)
.mapNotNull { if (cardActivationIsFinished(it)) null else it }

View file

@ -0,0 +1,27 @@
package com.tangem.domain.wallets.usecase
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.wallets.legacy.WalletsStateHolder
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
/**
* Use case that checks if wallet need backup cards
*/
class IsNeedToBackupUseCase(private val walletsStateHolder: WalletsStateHolder) {
operator fun invoke(id: UserWalletId): Flow<Boolean> {
val userWalletsListManager = requireNotNull(walletsStateHolder.userWalletsListManager)
return userWalletsListManager.userWallets
.map { wallets ->
val wallet = wallets.firstOrNull { it.walletId == id }
if (wallet == null) {
false
} else {
wallet.scanResponse.card.backupStatus is CardDTO.BackupStatus.NoBackup
}
}
}
}

View file

@ -43,6 +43,7 @@ internal object WalletPreviewData {
imageResId = R.drawable.ill_businessman_3d,
onRenameClick = { _, _ -> },
onDeleteClick = {},
cardCount = 1,
)
}
@ -65,6 +66,7 @@ internal object WalletPreviewData {
onDeleteClick = {},
balance = "8923,05 $",
additionalInfo = TextReference.Str("3 cards • Seed phrase"),
cardCount = 1,
)
}

View file

@ -34,6 +34,7 @@ internal sealed interface WalletCardState {
* @property onRenameClick lambda be invoked when Rename button is clicked
* @property onDeleteClick lambda be invoked when Delete button is clicked
* @property additionalInfo wallet additional info
* @property cardCount number of cards in the wallet
* @property balance wallet balance
*/
data class Content(
@ -43,6 +44,7 @@ internal sealed interface WalletCardState {
override val onRenameClick: (UserWalletId, String) -> Unit,
override val onDeleteClick: (UserWalletId) -> Unit,
val additionalInfo: TextReference,
val cardCount: Int?,
val balance: String,
) : WalletCardState
@ -55,6 +57,7 @@ internal sealed interface WalletCardState {
* @property onRenameClick lambda be invoked when Rename button is clicked
* @property onDeleteClick lambda be invoked when Delete button is clicked
* @property additionalInfo wallet additional info
* @property cardCount number of cards in the wallet
* @property balance wallet balance
*/
data class HiddenContent(
@ -65,6 +68,7 @@ internal sealed interface WalletCardState {
override val onDeleteClick: (UserWalletId) -> Unit,
val additionalInfo: TextReference,
val balance: String,
val cardCount: Int?,
) : WalletCardState
/**

View file

@ -11,6 +11,7 @@ import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount
import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState
import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState
import com.tangem.feature.wallet.presentation.wallet.state.WalletState
@ -96,6 +97,7 @@ internal class WalletSingleCurrencyLoadedBalanceConverter(
onRenameClick = selectedWallet.onRenameClick,
onDeleteClick = selectedWallet.onDeleteClick,
balance = formatFiatAmount(status = status, appCurrency = appCurrencyProvider()),
cardCount = currentWalletProvider().getCardsCount(),
)
}
is CryptoCurrencyStatus.Loading -> {

View file

@ -61,6 +61,13 @@ internal class WalletStateFactory(
private val hiddenStateConverter by lazy { HiddenStateConverter(currentStateProvider) }
private val walletUpdateCardCountConverter by lazy {
WalletUpdateCardCountConverter(
currentStateProvider,
currentWalletProvider,
)
}
private val tokenListErrorConverter by lazy {
TokenListErrorConverter(currentStateProvider)
}
@ -134,6 +141,8 @@ internal class WalletStateFactory(
fun getStateWithUpdatedWalletName(name: String): WalletState = walletRenameStateConverter.convert(value = name)
fun getStateWithUpdatedWalletCardCount(): WalletState = walletUpdateCardCountConverter.convert(Unit)
fun getUnlockedState(action: WalletsUpdateActionResolver.Action.UnlockWallet): WalletState {
return walletsUnlockStateConverter.convert(value = action)
}

View file

@ -0,0 +1,56 @@
package com.tangem.feature.wallet.presentation.wallet.state.factory
import com.tangem.common.Provider
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount
import com.tangem.feature.wallet.presentation.wallet.state.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.toImmutableList
internal class WalletUpdateCardCountConverter(
private val currentStateProvider: Provider<WalletState>,
private val currentWalletProvider: Provider<UserWallet>,
) : Converter<Unit, WalletState> {
override fun convert(value: Unit): WalletState {
return when (val state = currentStateProvider()) {
is WalletState.ContentState -> {
state.copySealed(
walletsListConfig = state.walletsListConfig.refreshCardCount(),
)
}
is WalletState.Initial -> state
}
}
private fun WalletsListConfig.refreshCardCount(): WalletsListConfig {
return copy(
wallets = wallets
.mapIndexed { index, walletCard ->
if (index == selectedWalletIndex) {
when (walletCard) {
is WalletCardState.Content -> walletCard.copy(
additionalInfo = WalletAdditionalInfoFactory.resolve(
wallet = currentWalletProvider(),
),
cardCount = currentWalletProvider().getCardsCount(),
)
is WalletCardState.HiddenContent -> walletCard.copy(
additionalInfo = WalletAdditionalInfoFactory.resolve(
wallet = currentWalletProvider(),
),
cardCount = currentWalletProvider().getCardsCount(),
)
else -> walletCard
}
} else {
walletCard
}
}
.toImmutableList(),
)
}
}

View file

@ -6,6 +6,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.TokenList.FiatBalance
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState
import com.tangem.utils.converter.Converter
@ -54,6 +55,7 @@ internal class FiatBalanceToWalletCardConverter(
fiatCurrencySymbol = appCurrency.symbol,
),
additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = currentWalletProvider()),
cardCount = currentWalletProvider().getCardsCount(),
)
} else {
WalletCardState.Content(
@ -68,6 +70,7 @@ internal class FiatBalanceToWalletCardConverter(
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
),
cardCount = currentWalletProvider().getCardsCount(),
)
}
}

View file

@ -25,6 +25,7 @@ internal class WalletHiddenBalanceStateConverter {
onRenameClick = content.onRenameClick,
onDeleteClick = content.onDeleteClick,
balance = content.balance,
cardCount = content.cardCount,
)
}
@ -37,6 +38,7 @@ internal class WalletHiddenBalanceStateConverter {
onRenameClick = hiddenContent.onRenameClick,
onDeleteClick = hiddenContent.onDeleteClick,
balance = hiddenContent.balance,
cardCount = hiddenContent.cardCount,
)
}
}

View file

@ -6,6 +6,8 @@ import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
@ -16,9 +18,10 @@ import kotlin.collections.count
/**
* Wallet notifications list factory
*
* @property isDemoCardUseCase use case that check if card is demo
* @property isReadyToShowRateAppUseCase use case that check if card is user already rate app
* @property wasCardScannedUseCase use case that check if card was scanned
* @property isDemoCardUseCase use case that checks if card is demo
* @property isReadyToShowRateAppUseCase use case that checks if card is user already rate app
* @property wasCardScannedUseCase use case that checks if card was scanned
* @property isNeedToBackupUseCase use case that checks if wallet need backup cards
* @property clickIntents screen click intents
*
[REDACTED_AUTHOR]
@ -27,17 +30,20 @@ internal class WalletNotificationsListFactory(
private val isDemoCardUseCase: IsDemoCardUseCase,
private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
private val wasCardScannedUseCase: WasCardScannedUseCase,
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
private val clickIntents: WalletClickIntents,
) {
fun create(
selectedWalletId: UserWalletId,
cardTypesResolver: CardTypesResolver,
cryptoCurrencyList: List<CryptoCurrencyStatus>,
): Flow<ImmutableList<WalletNotification>> {
return combine(
flow = wasCardScannedUseCase(cardTypesResolver.getCardId()),
flow2 = isReadyToShowRateAppUseCase(),
) { wasCardScanned, isReadyToShowRating ->
flow3 = isNeedToBackupUseCase(selectedWalletId),
) { wasCardScanned, isReadyToShowRating, isNeedToBackup ->
buildList {
addCriticalNotifications(cardTypesResolver)
@ -45,7 +51,7 @@ internal class WalletNotificationsListFactory(
addRateTheAppNotification(isReadyToShowRating)
addWarningNotifications(cardTypesResolver, cryptoCurrencyList, wasCardScanned)
addWarningNotifications(cardTypesResolver, cryptoCurrencyList, wasCardScanned, isNeedToBackup)
}.toImmutableList()
}
}
@ -116,12 +122,13 @@ internal class WalletNotificationsListFactory(
cardTypesResolver: CardTypesResolver,
cryptoCurrencyList: List<CryptoCurrencyStatus>,
wasCardScanned: Boolean,
isNeedToBackup: Boolean,
) {
addIf(
element = WalletNotification.Warning.MissingBackup(
onStartBackupClick = clickIntents::onBackupCardClick,
),
condition = !cardTypesResolver.isBackupForbidden() && !cardTypesResolver.hasBackup(),
condition = isNeedToBackup,
)
val isDemo = isDemoCardUseCase(cardId = cardTypesResolver.getCardId())

View file

@ -122,6 +122,7 @@ internal class WalletViewModel @Inject constructor(
wasCardScannedUseCase: WasCardScannedUseCase,
isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
isDemoCardUseCase: IsDemoCardUseCase,
isNeedToBackupUseCase: IsNeedToBackupUseCase,
// endregion Parameters
) : ViewModel(), DefaultLifecycleObserver, WalletClickIntents {
@ -135,6 +136,7 @@ internal class WalletViewModel @Inject constructor(
wasCardScannedUseCase = wasCardScannedUseCase,
isReadyToShowRateAppUseCase = isReadyToShowRateAppUseCase,
isDemoCardUseCase = isDemoCardUseCase,
isNeedToBackupUseCase = isNeedToBackupUseCase,
clickIntents = this,
)
@ -227,6 +229,9 @@ internal class WalletViewModel @Inject constructor(
is WalletsUpdateActionResolver.Action.AddWallet -> {
scrollAndUpdateState(action.selectedWalletIndex)
}
is WalletsUpdateActionResolver.Action.UpdateWalletCardCount -> {
uiState = stateFactory.getStateWithUpdatedWalletCardCount()
}
is WalletsUpdateActionResolver.Action.Unknown -> Unit
}
}
@ -983,6 +988,7 @@ internal class WalletViewModel @Inject constructor(
private fun updateNotifications(index: Int, tokenList: TokenList? = null) {
notificationsListFactory.create(
selectedWalletId = getWallet(index).walletId,
cardTypesResolver = getCardTypeResolver(index = index),
cryptoCurrencyList = if (tokenList != null) {
when (tokenList) {

View file

@ -4,6 +4,7 @@ import com.tangem.common.Provider
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount
import com.tangem.feature.wallet.presentation.wallet.state.WalletLockedState
import com.tangem.feature.wallet.presentation.wallet.state.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState
@ -110,20 +111,29 @@ internal class WalletsUpdateActionResolver(
selectedWallet: UserWallet,
): Action {
val selectedWalletName = selectedWallet.name
val previousWalletState = state.getPrevSelectedWallet()
return when {
previousWalletState.title != selectedWalletName -> {
Action.UpdateWalletName(selectedWalletName)
}
if (state.getPrevSelectedWallet().title != selectedWalletName) {
return Action.UpdateWalletName(selectedWalletName)
state is WalletLockedState && !selectedWallet.isLocked -> {
Action.UnlockWallet(
selectedWalletIndex = wallets.indexOfWallet(id = selectedWallet.walletId),
selectedWallet = selectedWallet,
unlockedWallets = wallets.filterNot(UserWallet::isLocked),
)
}
previousWalletState is WalletCardState.Content &&
previousWalletState.cardCount != selectedWallet.getCardsCount() ||
previousWalletState is WalletCardState.HiddenContent &&
previousWalletState.cardCount != selectedWallet.getCardsCount() -> {
Action.UpdateWalletCardCount
}
else -> Action.Unknown
}
if (state is WalletLockedState && !selectedWallet.isLocked) {
return Action.UnlockWallet(
selectedWalletIndex = wallets.indexOfWallet(id = selectedWallet.walletId),
selectedWallet = selectedWallet,
unlockedWallets = wallets.filterNot(UserWallet::isLocked),
)
}
return Action.Unknown
}
private fun WalletState.ContentState.getPrevSelectedWallet(): WalletCardState {
@ -163,6 +173,8 @@ internal class WalletsUpdateActionResolver(
data class AddWallet(val selectedWalletIndex: Int) : Action()
object UpdateWalletCardCount : Action()
object Unknown : Action()
}
}