diff --git a/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt index 4f41cea873..918b41ff04 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt @@ -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) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt index 2ab9ded42f..e686d2f730 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -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 } diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt index 6f5615c1b6..e1d3826183 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt @@ -40,7 +40,6 @@ internal class DefaultNetworksRepository( override fun getNetworkStatusesUpdates( userWalletId: UserWalletId, networks: Set, - refresh: Boolean, ): Flow> = channelFlow { launch(dispatchers.io) { networksStatusesStore.get(userWalletId) @@ -48,7 +47,7 @@ internal class DefaultNetworksRepository( } withContext(dispatchers.io) { - fetchNetworksStatusesIfCacheExpired(userWalletId, networks, refresh) + fetchNetworksStatusesIfCacheExpired(userWalletId, networks, false) } }.cancellable() diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyStatusUpdatesUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyStatusUpdatesUseCase.kt index f3cf282b1d..d018b74e85 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyStatusUpdatesUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyStatusUpdatesUseCase.kt @@ -37,17 +37,15 @@ class GetCurrencyStatusUpdatesUseCase( operator fun invoke( userWalletId: UserWalletId, currencyId: CryptoCurrency.ID, - refresh: Boolean, ): Flow> { return flow { - emitAll(getCurrency(userWalletId, currencyId, refresh)) + emitAll(getCurrency(userWalletId, currencyId)) }.flowOn(dispatchers.io) } private suspend fun getCurrency( userWalletId: UserWalletId, currencyId: CryptoCurrency.ID, - refresh: Boolean, ): Flow> { val operations = CurrenciesStatusesOperations( currenciesRepository = currenciesRepository, @@ -56,7 +54,7 @@ class GetCurrencyStatusUpdatesUseCase( userWalletId = userWalletId, ) - return operations.getCurrencyStatusFlow(currencyId, refresh).map { maybeCurrency -> + return operations.getCurrencyStatusFlow(currencyId).map { maybeCurrency -> maybeCurrency.mapLeft(CurrenciesStatusesOperations.Error::mapToCurrencyError) } } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt index 3a2327b7cd..fc4ec95f89 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt @@ -67,16 +67,13 @@ internal class CurrenciesStatusesOperations( } } - suspend fun getCurrencyStatusFlow( - currencyId: CryptoCurrency.ID, - refresh: Boolean = false, - ): Flow> { + suspend fun getCurrencyStatusFlow(currencyId: CryptoCurrency.ID): Flow> { val currency = recover( block = { getMultiCurrencyWalletCurrency(currencyId) }, recover = { return flowOf(it.left()) }, ) - return getCurrencyStatusFlow(currency, refresh) + return getCurrencyStatusFlow(currency) } suspend fun getNetworkCoinFlow(networkId: Network.ID): Flow> { @@ -97,10 +94,7 @@ internal class CurrenciesStatusesOperations( return getCurrencyStatusFlow(currency) } - private fun getCurrencyStatusFlow( - currency: CryptoCurrency, - refresh: Boolean = false, - ): Flow> { + private fun getCurrencyStatusFlow(currency: CryptoCurrency): Flow> { val (networks, currenciesIds) = getIds(nonEmptyListOf(currency)) val quoteFlow = getQuotes(currenciesIds) @@ -111,7 +105,7 @@ internal class CurrenciesStatusesOperations( } } - val statusFlow = getNetworksStatuses(networks, refresh) + val statusFlow = getNetworksStatuses(networks) .map { maybeStatuses -> maybeStatuses.flatMap { statuses -> statuses.singleOrNull { it.network == currency.network }?.right() @@ -210,11 +204,8 @@ internal class CurrenciesStatusesOperations( .onEmpty { emit(Error.EmptyQuotes.left()) } } - private fun getNetworksStatuses( - networks: NonEmptySet, - refresh: Boolean = false, - ): Flow>> { - return networksRepository.getNetworkStatusesUpdates(userWalletId, networks, refresh) + private fun getNetworksStatuses(networks: NonEmptySet): Flow>> { + return networksRepository.getNetworkStatusesUpdates(userWalletId, networks) .map, Either>> { it.right() } .catch { emit(Error.DataError(it).left()) } .onEmpty { emit(Error.EmptyNetworksStatuses.left()) } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt index da67708111..30baab37f3 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt @@ -16,14 +16,9 @@ interface NetworksRepository { * * @param userWalletId The unique identifier of the user wallet. * @param networks A set of network which statuses are to be retrieved. - * @param refresh A boolean flag indicating whether the data should be refreshed from remote. * @return A [Flow] emitting a set of [NetworkStatus] objects corresponding to the specified networks. */ - fun getNetworkStatusesUpdates( - userWalletId: UserWalletId, - networks: Set, - refresh: Boolean = false, - ): Flow> + fun getNetworkStatusesUpdates(userWalletId: UserWalletId, networks: Set): Flow> /** * Retrieves network statuses of specified blockchain networks for a specific user wallet. diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockNetworksRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockNetworksRepository.kt index 363ad6202a..ffefd750e0 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockNetworksRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockNetworksRepository.kt @@ -17,7 +17,6 @@ internal class MockNetworksRepository( override fun getNetworkStatusesUpdates( userWalletId: UserWalletId, networks: Set, - refresh: Boolean, ): Flow> { return statuses.map { it.getOrElse { e -> throw e } } } diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/IsNeedToBackupUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/IsNeedToBackupUseCase.kt new file mode 100644 index 0000000000..24fdc5a232 --- /dev/null +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/IsNeedToBackupUseCase.kt @@ -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 { + 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 + } + } + } +} \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsNotification.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsNotification.kt index 5d7693bd08..13b31508f4 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsNotification.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsNotification.kt @@ -11,14 +11,12 @@ import com.tangem.features.tokendetails.impl.R // TODO: Finalize notification strings [REDACTED_JIRA] @Immutable sealed class TokenDetailsNotification( - open val isVisible: Boolean = true, open val config: NotificationConfig, ) { data class RentInfo( private val rentInfo: CryptoCurrencyWarning.Rent, private val onCloseClick: () -> Unit, - override val isVisible: Boolean = true, ) : TokenDetailsNotification( config = NotificationConfig( title = TextReference.Res(R.string.send_network_fee_title), diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsNotificationConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsNotificationConverter.kt index 25f691722d..945fd6107e 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsNotificationConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsNotificationConverter.kt @@ -1,11 +1,11 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory -import com.tangem.common.extensions.cast import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents import com.tangem.utils.converter.Converter +import com.tangem.utils.extensions.removeBy import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList @@ -17,15 +17,9 @@ internal class TokenDetailsNotificationConverter( return value.map(::mapToNotification).toImmutableList() } - fun getStateRentInfoVisibility( - currentState: TokenDetailsState, - isVisible: Boolean, - ): ImmutableList { + fun removeRentInfo(currentState: TokenDetailsState): ImmutableList { val newNotifications = currentState.notifications.toMutableList() - val oldNotification = newNotifications.find { it is TokenDetailsNotification.RentInfo } - oldNotification?.let { - newNotifications.add(it.cast().copy(isVisible = isVisible)) - } + newNotifications.removeBy { it is TokenDetailsNotification.RentInfo } return newNotifications.toImmutableList() } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt index 40f0b4da65..053b87757c 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt @@ -139,9 +139,7 @@ internal class TokenDetailsStateFactory( } fun getRefreshedState(): TokenDetailsState { - val state = currentStateProvider() return refreshStateConverter.convert(false) - .copy(notifications = notificationConverter.getStateRentInfoVisibility(state, true)) } fun getStateWithReceiveBottomSheet( @@ -212,6 +210,6 @@ internal class TokenDetailsStateFactory( fun getStateWithRemovedRentNotification(): TokenDetailsState { val state = currentStateProvider() - return state.copy(notifications = notificationConverter.getStateRentInfoVisibility(state, false)) + return state.copy(notifications = notificationConverter.removeRentInfo(state)) } } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt index d1683f22c8..288f8e1464 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt @@ -87,7 +87,7 @@ internal fun TokenDetailsScreen(state: TokenDetailsState) { ) } items( - items = state.notifications.filter { it.isVisible }, + items = state.notifications, key = { it.config::class.java }, contentType = { it.config::class.java }, itemContent = { Notification(config = it.config, modifier = itemModifier.animateItemPlacement()) }, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index f37726978c..ab5d7f11d0 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -35,6 +35,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.JobHolder import com.tangem.utils.coroutines.saveIn import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import timber.log.Timber @@ -71,6 +72,7 @@ internal class TokenDetailsViewModel @Inject constructor( private val marketPriceJobHolder = JobHolder() private val refreshStateJobHolder = JobHolder() + private val networkStatusAutoUpdateStateJobHolder = JobHolder() private var cryptoCurrencyStatus: CryptoCurrencyStatus? = null private var wallet by Delegates.notNull() @@ -148,7 +150,6 @@ internal class TokenDetailsViewModel @Inject constructor( getCurrencyStatusUpdatesUseCase( userWalletId = selectedWallet.walletId, currencyId = cryptoCurrency.id, - refresh = true, ) .distinctUntilChanged() .onEach { either -> @@ -161,6 +162,17 @@ internal class TokenDetailsViewModel @Inject constructor( .flowOn(dispatchers.io) .launchIn(viewModelScope) .saveIn(marketPriceJobHolder) + + viewModelScope.launch(dispatchers.io) { + // Wait for blockchain updates pending transactions. + // Immediate update doesn't receive any changes. + delay(NETWORK_STATUS_AUTO_UPDATE_DELAY) + fetchCurrencyStatusUseCase.invoke( + userWalletId = wallet.walletId, + id = cryptoCurrency.id, + refresh = true, + ) + }.saveIn(networkStatusAutoUpdateStateJobHolder) } private fun updateTxHistory(refresh: Boolean = false) { @@ -365,7 +377,7 @@ internal class TokenDetailsViewModel @Inject constructor( refresh = true, ) updateTxHistory(refresh = true) - + updateWarnings(wallet) uiState = stateFactory.getRefreshedState() }.saveIn(refreshStateJobHolder) } @@ -377,4 +389,8 @@ internal class TokenDetailsViewModel @Inject constructor( override fun onCloseRentInfoNotification() { uiState = stateFactory.getStateWithRemovedRentNotification() } + + companion object { + private const val NETWORK_STATUS_AUTO_UPDATE_DELAY = 1000L + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index 0671449433..59e36af357 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -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, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt index c9830c2a7b..c968de0013 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt @@ -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 /** diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt index ff8df226a1..ccfbe015bc 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt @@ -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 -> { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt index 92783093a4..a46a50cc2c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt @@ -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) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletUpdateCardCountConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletUpdateCardCountConverter.kt new file mode 100644 index 0000000000..805f3168b4 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletUpdateCardCountConverter.kt @@ -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, + private val currentWalletProvider: Provider, +) : Converter { + + 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(), + ) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt index 89e3ff8be5..bca1082c42 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt @@ -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(), ) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/WalletHiddenBalanceStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/WalletHiddenBalanceStateConverter.kt index 7f29843cea..eb85c8f42e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/WalletHiddenBalanceStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/WalletHiddenBalanceStateConverter.kt @@ -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, ) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt index 928b02cc55..8c0fa79e8d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt @@ -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, ): Flow> { 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, 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()) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 015c3e99a5..f0c38778bf 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -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) { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletsUpdateActionResolver.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletsUpdateActionResolver.kt index 101f025d4e..bc8aa4f61a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletsUpdateActionResolver.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletsUpdateActionResolver.kt @@ -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() } } \ No newline at end of file