Updated on 2026-08-14

This commit is contained in:
Tangem 2025-11-05 14:01:26 +03:00
commit 9aacdb2ed3
695 changed files with 22022 additions and 5116 deletions

View file

@ -9,6 +9,7 @@ import com.tangem.core.analytics.models.AnalyticsParam
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.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.models.wallet.UserWallet
@ -18,6 +19,7 @@ import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.nft.ObserveAndClearNFTCacheIfNeedUseCase
import com.tangem.domain.notifications.GetIsHuaweiDeviceWithoutGoogleServicesUseCase
import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.pay.usecase.TangemPayIssueOrderUseCase
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
import com.tangem.domain.settings.*
@ -93,7 +95,9 @@ internal class WalletModel @Inject constructor(
private val tangemPayIssueOrderUseCase: TangemPayIssueOrderUseCase,
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
private val yieldSupplyApyUpdateUseCase: YieldSupplyApyUpdateUseCase,
private val tangemPayOnboardingRepository: OnboardingRepository,
private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles,
private val accountsFeatureToggles: AccountsFeatureToggles,
val screenLifecycleProvider: ScreenLifecycleProvider,
val innerWalletRouter: InnerWalletRouter,
) : Model() {
@ -344,16 +348,24 @@ internal class WalletModel @Inject constructor(
* and every minute while user stays on the main screen
*/
screenLifecycleProvider.isBackgroundState.onEach { inBackground ->
// fast exit
if (!tangemPayFeatureToggles.isTangemPayEnabled) return@onEach
updateTangemPayJobHolder.cancel()
if (!inBackground && tangemPayFeatureToggles.isTangemPayEnabled) {
modelScope.launch {
modelScope.launch {
// fast exit
val initialDataProduced = tangemPayOnboardingRepository.isTangemPayInitialDataProduced()
if (!initialDataProduced) return@launch
if (!inBackground) {
refreshTangemPayInfo()
while (isActive) {
delay(TANGEM_PAY_UPDATE_INTERVAL)
refreshTangemPayInfo()
}
}.saveIn(updateTangemPayJobHolder)
}
}
}.saveIn(updateTangemPayJobHolder)
}.launchIn(modelScope)
}
@ -365,7 +377,12 @@ internal class WalletModel @Inject constructor(
value = info,
onClickIssue = ::issueOrder,
onClickKyc = innerWalletRouter::openTangemPayOnboarding,
openDetails = innerWalletRouter::openTangemPayDetails,
openDetails = { config ->
innerWalletRouter.openTangemPayDetails(
userWalletId = stateHolder.getSelectedWalletId(),
config = config,
)
},
),
)
}
@ -516,21 +533,39 @@ internal class WalletModel @Inject constructor(
}
private suspend fun addWallet(action: WalletsUpdateActionResolver.Action.AddWallet) {
walletScreenContentLoader.load(
userWallet = action.selectedWallet,
clickIntents = clickIntents,
coroutineScope = modelScope,
)
if (accountsFeatureToggles.isFeatureEnabled) {
fetchWalletContent(userWallet = action.selectedWallet)
fetchWalletContent(userWallet = action.selectedWallet)
stateHolder.update(
AddWalletTransformer(
userWallet = action.selectedWallet,
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
),
)
stateHolder.update(
AddWalletTransformer(
walletScreenContentLoader.load(
userWallet = action.selectedWallet,
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
),
)
coroutineScope = modelScope,
)
} else {
walletScreenContentLoader.load(
userWallet = action.selectedWallet,
clickIntents = clickIntents,
coroutineScope = modelScope,
)
fetchWalletContent(userWallet = action.selectedWallet)
stateHolder.update(
AddWalletTransformer(
userWallet = action.selectedWallet,
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
),
)
}
scrollToWallet(prevIndex = action.prevWalletIndex, newIndex = action.selectedWalletIndex) {
stateHolder.update {

View file

@ -24,7 +24,7 @@ import com.tangem.core.ui.haptic.TangemHapticEffect
import com.tangem.core.ui.haptic.VibratorHapticManager
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
import com.tangem.domain.account.status.usecase.SaveCryptoCurrenciesUseCase
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.extenstions.unwrap
import com.tangem.domain.core.lce.Lce
@ -157,7 +157,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
private val removeCurrencyUseCase: RemoveCurrencyUseCase,
private val accountsFeatureToggles: AccountsFeatureToggles,
private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase,
private val saveCryptoCurrenciesUseCase: SaveCryptoCurrenciesUseCase,
private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
) : BaseWalletClickIntents(), WalletCurrencyActionsClickIntents {
override fun onSendClick(
@ -360,7 +360,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
return@launch
}
saveCryptoCurrenciesUseCase(accountId = accountId, remove = cryptoCurrencyStatus.currency)
manageCryptoCurrenciesUseCase(accountId = accountId, remove = cryptoCurrencyStatus.currency)
} else {
removeCurrencyUseCase(userWalletId, cryptoCurrencyStatus.currency)
}
@ -507,7 +507,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
appRouter.push(
AppRoute.Staking(
userWalletId = userWalletId,
cryptoCurrencyId = cryptoCurrency.id,
cryptoCurrency = cryptoCurrency,
yieldId = yield?.id ?: return@launch,
),
)

View file

@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.account
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.account.status.supplier.SingleAccountStatusSupplier
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
import javax.inject.Inject
@ -12,4 +13,5 @@ internal class AccountDependencies @Inject constructor(
val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
val expandedAccountsHolder: ExpandedAccountsHolder,
val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
val singleAccountStatusSupplier: SingleAccountStatusSupplier,
)

View file

@ -20,7 +20,7 @@ internal class ExpandedAccountsHolder @Inject constructor(
fun expandedAccounts(userWallet: UserWallet): Flow<Set<AccountId>> = channelFlow {
walletAccounts(userWallet)
.onEach { accountList ->
val isSingleAccount = accountList.totalAccounts == 1
val isSingleAccount = accountList.accounts.size == 1
val defaultExpanded = when {
isSingleAccount -> setOf(accountList.mainAccount.accountId)
else -> setOf()

View file

@ -106,6 +106,28 @@ internal object WalletScreenPreviewData {
),
)
private val emptyPortfolioContentState = WalletTokensListState.ContentState.PortfolioContent(
items = persistentListOf(
TokensListItemUM.Portfolio(
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>().toPersistentList(),
isExpanded = false,
isCollapsable = true,
tokenItemUM = AccountItemPreviewData.accountItem
.copy(iconState = AccountItemPreviewData.accountLetterIcon),
),
TokensListItemUM.Portfolio(
tokens = persistentListOf(),
isExpanded = true,
isCollapsable = true,
tokenItemUM = AccountItemPreviewData.accountItem,
),
),
organizeTokensButtonConfig = WalletTokensListState.OrganizeTokensButtonConfig(
isEnabled = true,
onClick = {},
),
)
private val noteLockedCard by lazy {
WalletCardState.LockedContent(
id = UserWalletId(stringValue = "1"),
@ -208,4 +230,12 @@ internal object WalletScreenPreviewData {
multiWalletState.copy(tokensListState = portfolioContentState),
),
)
internal val accountScreenWithEmptyTokensState =
walletScreenState.copy(
wallets = persistentListOf(
singleWalletLockedState,
multiWalletState.copy(tokensListState = emptyPortfolioContentState),
),
)
}

View file

@ -109,8 +109,8 @@ internal class DefaultWalletRouter @Inject constructor(
router.push(AppRoute.TangemPayOnboarding(AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding))
}
override fun openTangemPayDetails(config: TangemPayDetailsConfig) {
router.push(AppRoute.TangemPayDetails(config))
override fun openTangemPayDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig) {
router.push(AppRoute.TangemPayDetails(userWalletId = userWalletId, config = config))
}
override fun openYieldSupplyBottomSheet(

View file

@ -60,7 +60,7 @@ internal interface InnerWalletRouter {
fun openTangemPayOnboarding()
fun openTangemPayDetails(config: TangemPayDetailsConfig)
fun openTangemPayDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig)
/** Open BS abput yield supply active and all money deposited in AAVE */
fun openYieldSupplyBottomSheet(

View file

@ -1,11 +1,17 @@
package com.tangem.feature.wallet.presentation.wallet.domain
import arrow.core.Either
import arrow.core.right
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.account.status.producer.SingleAccountStatusProducer
import com.tangem.domain.account.status.supplier.SingleAccountStatusSupplier
import com.tangem.domain.card.CardTypesResolver
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
@ -22,8 +28,11 @@ import kotlinx.coroutines.flow.*
import javax.inject.Inject
@ModelScoped
@Suppress("LongParameterList")
internal class GetSingleWalletWarningsFactory @Inject constructor(
private val accountsFeatureToggles: AccountsFeatureToggles,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val singleAccountStatusSupplier: SingleAccountStatusSupplier,
private val isDemoCardUseCase: IsDemoCardUseCase,
private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
@ -40,7 +49,7 @@ internal class GetSingleWalletWarningsFactory @Inject constructor(
val cardTypesResolver = userWallet.scanResponse.cardTypesResolver
return combine(
flow = getSingleCryptoCurrencyStatusUseCase.invokeSingleWallet(userWallet.walletId),
flow = getPrimaryCurrencyStatusFlow(userWallet),
flow2 = isReadyToShowRateAppUseCase().conflate(),
flow3 = isNeedToBackupUseCase(userWallet.walletId).conflate(),
flow4 = getWalletsUseCase().conflate(),
@ -217,6 +226,29 @@ internal class GetSingleWalletWarningsFactory @Inject constructor(
}
}
private fun getPrimaryCurrencyStatusFlow(
userWallet: UserWallet,
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
return if (accountsFeatureToggles.isFeatureEnabled) {
getAccountStatusFlow(userWallet).mapNotNull { accountStatus ->
accountStatus.flattenCurrencies().firstOrNull()
}
.distinctUntilChanged()
.conflate()
.map { it.right() }
} else {
getSingleCryptoCurrencyStatusUseCase.invokeSingleWallet(userWallet.walletId)
}
}
private fun getAccountStatusFlow(userWallet: UserWallet): Flow<AccountStatus> {
val accountId = AccountId.forMainCryptoPortfolio(userWalletId = userWallet.walletId)
return singleAccountStatusSupplier(SingleAccountStatusProducer.Params(accountId))
.distinctUntilChanged()
.conflate()
}
private companion object {
const val NOTE_MIGRATION_URL = "https://tangem.com/en/?promocode=Note10"
const val MAX_REMAINING_SIGNATURES_COUNT = 10

View file

@ -1,21 +1,37 @@
package com.tangem.feature.wallet.presentation.wallet.domain
import com.tangem.common.extensions.isZero
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.tokenlist.TokenList
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
@ModelScoped
internal class WalletWithFundsChecker @Inject constructor(
private val setWalletWithFundsFoundUseCase: SetWalletWithFundsFoundUseCase,
) {
private val statusByWalletId = ConcurrentHashMap<UserWalletId, Boolean>()
suspend fun check(tokenList: TokenList) {
val hasNonZeroWallets = tokenList.flattenCurrencies().hasNonZeroWallets()
if (hasNonZeroWallets) setWalletWithFundsFoundUseCase()
}
suspend fun check(userWalletId: UserWalletId, currencies: List<CryptoCurrencyStatus>) {
val hasNonZeroWallets = currencies.hasNonZeroWallets()
val prevStatus = statusByWalletId.get(userWalletId)
if (hasNonZeroWallets && prevStatus != true) {
statusByWalletId[userWalletId] = true
setWalletWithFundsFoundUseCase()
}
}
private fun List<CryptoCurrencyStatus>.hasNonZeroWallets(): Boolean {
return any {
val amount = it.value.amount ?: return@any false

View file

@ -1,6 +1,7 @@
package com.tangem.feature.wallet.presentation.wallet.loaders
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isMultiCurrency
@ -8,11 +9,15 @@ import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.loaders.implementors.*
import javax.inject.Inject
@Suppress("LongParameterList")
@ModelScoped
internal class WalletContentLoaderFactory @Inject constructor(
private val multiWalletContentLoaderFactory: MultiWalletContentLoaderFactory,
private val singleWalletWithTokenContentLoaderFactory: SingleWalletWithTokenContentLoaderFactory,
private val singleWalletWithTokenContentLoaderV2Factory: SingleWalletWithTokenContentLoaderV2.Factory,
private val accountsFeatureToggles: AccountsFeatureToggles,
private val singleWalletContentLoaderFactory: SingleWalletContentLoaderFactory,
private val singleWalletContentLoaderV2Factory: SingleWalletContentLoaderV2.Factory,
private val visaWalletContentLoaderFactory: VisaWalletContentLoaderFactory,
) {
@ -26,13 +31,21 @@ internal class WalletContentLoaderFactory @Inject constructor(
multiWalletContentLoaderFactory.create(userWallet, clickIntents)
}
userWallet is UserWallet.Cold && userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken() -> {
singleWalletWithTokenContentLoaderFactory.create(userWallet, clickIntents)
if (accountsFeatureToggles.isFeatureEnabled) {
singleWalletWithTokenContentLoaderV2Factory.create(userWallet)
} else {
singleWalletWithTokenContentLoaderFactory.create(userWallet, clickIntents)
}
}
userWallet is UserWallet.Cold && userWallet.scanResponse.cardTypesResolver.isVisaWallet() -> {
visaWalletContentLoaderFactory.create(userWallet, clickIntents, isRefresh)
}
userWallet is UserWallet.Cold && !userWallet.isMultiCurrency -> {
singleWalletContentLoaderFactory.create(userWallet, clickIntents, isRefresh)
if (accountsFeatureToggles.isFeatureEnabled) {
singleWalletContentLoaderV2Factory.create(userWallet, isRefresh)
} else {
singleWalletContentLoaderFactory.create(userWallet, clickIntents, isRefresh)
}
}
else -> null
}

View file

@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.onramp.GetOnrampTransactionsUseCase
import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase
import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase
@ -9,10 +10,8 @@ import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.account.AccountDependencies
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.GetSingleWalletWarningsFactory
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
@ -36,7 +35,6 @@ internal class SingleWalletContentLoader(
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
private val accountDependencies: AccountDependencies,
) : WalletContentLoader(id = userWallet.walletId) {
override fun create(): List<WalletSubscriber> {
@ -55,7 +53,6 @@ internal class SingleWalletContentLoader(
clickIntents = clickIntents,
getSingleCryptoCurrencyStatusUseCase = getSingleCryptoCurrencyStatusUseCase,
getCryptoCurrencyActionsUseCase = getCryptoCurrencyActionsUseCase,
accountDependencies = accountDependencies,
),
SingleWalletNotificationsSubscriber(
userWallet = userWallet,

View file

@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.onramp.GetOnrampTransactionsUseCase
import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase
import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase
@ -10,10 +11,8 @@ import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.account.AccountDependencies
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.GetSingleWalletWarningsFactory
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
@ -21,6 +20,7 @@ import javax.inject.Inject
@ModelScoped
@Suppress("LongParameterList")
@Deprecated("Use SingleWalletContentLoaderV2.Factory instead")
internal class SingleWalletContentLoaderFactory @Inject constructor(
private val stateHolder: WalletStateController,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
@ -35,14 +35,12 @@ internal class SingleWalletContentLoaderFactory @Inject constructor(
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
private val accountDependencies: AccountDependencies,
) {
fun create(userWallet: UserWallet.Cold, clickIntents: WalletClickIntents, isRefresh: Boolean): WalletContentLoader {
return SingleWalletContentLoader(
userWallet = userWallet,
clickIntents = clickIntents,
accountDependencies = accountDependencies,
isRefresh = isRefresh,
stateHolder = stateHolder,
getSingleCryptoCurrencyStatusUseCase = getSingleCryptoCurrencyStatusUseCase,

View file

@ -0,0 +1,103 @@
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.domain.account.status.usecase.GetCryptoCurrencyActionsUseCaseV2
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.onramp.GetOnrampTransactionsUseCase
import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.account.AccountDependencies
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.GetSingleWalletWarningsFactory
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@Suppress("LongParameterList")
internal class SingleWalletContentLoaderV2 @AssistedInject constructor(
@Assisted private val userWallet: UserWallet.Cold,
@Assisted private val isRefresh: Boolean,
private val clickIntents: WalletClickIntents,
private val stateHolder: WalletStateController,
private val getCryptoCurrencyActionsUseCaseV2: GetCryptoCurrencyActionsUseCaseV2,
private val getSingleWalletWarningsFactory: GetSingleWalletWarningsFactory,
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase,
private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase,
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
private val accountDependencies: AccountDependencies,
private val walletWithFundsChecker: WalletWithFundsChecker,
private val dispatchers: CoroutineDispatcherProvider,
) : WalletContentLoader(id = userWallet.walletId) {
override fun create(): List<WalletSubscriber> = listOf(
PrimaryCurrencySubscriberV2(
userWallet = userWallet,
singleAccountStatusListSupplier = accountDependencies.singleAccountStatusListSupplier,
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
stateController = stateHolder,
analyticsEventHandler = analyticsEventHandler,
),
SingleWalletButtonsSubscriberV2(
userWallet = userWallet,
singleAccountStatusListSupplier = accountDependencies.singleAccountStatusListSupplier,
stateController = stateHolder,
clickIntents = clickIntents,
getCryptoCurrencyActionsUseCaseV2 = getCryptoCurrencyActionsUseCaseV2,
),
SingleWalletNotificationsSubscriber(
userWallet = userWallet,
stateHolder = stateHolder,
clickIntents = clickIntents,
getSingleWalletWarningsFactory = getSingleWalletWarningsFactory,
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
),
WalletDropDownItemsSubscriber(
stateHolder = stateHolder,
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
clickIntents = clickIntents,
),
SingleWalletExpressStatusesSubscriberV2(
userWallet = userWallet,
singleAccountStatusListSupplier = accountDependencies.singleAccountStatusListSupplier,
getOnrampTransactionsUseCase = getOnrampTransactionsUseCase,
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
onrampRemoveTransactionUseCase = onrampRemoveTransactionUseCase,
stateController = stateHolder,
clickIntents = clickIntents,
analyticsEventHandler = analyticsEventHandler,
),
TxHistorySubscriberV2(
userWallet = userWallet,
singleAccountStatusListSupplier = accountDependencies.singleAccountStatusListSupplier,
txHistoryItemsCountUseCase = txHistoryItemsCountUseCase,
txHistoryItemsUseCase = txHistoryItemsUseCase,
isRefresh = isRefresh,
stateController = stateHolder,
clickIntents = clickIntents,
),
CheckWalletWithFundsSubscriber(
userWallet = userWallet,
singleAccountStatusListSupplier = accountDependencies.singleAccountStatusListSupplier,
walletWithFundsChecker = walletWithFundsChecker,
dispatchers = dispatchers,
),
)
@AssistedFactory
interface Factory {
fun create(userWallet: UserWallet.Cold, isRefresh: Boolean): SingleWalletContentLoaderV2
}
}

View file

@ -1,12 +1,13 @@
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.staking.usecase.StakingApyFlowUseCase
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.account.AccountDependencies
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender
@ -15,8 +16,8 @@ import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenList
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
import com.tangem.feature.wallet.presentation.account.AccountDependencies
@Deprecated("Use SingleWalletWithTokenContentLoaderV2 instead")
@Suppress("LongParameterList")
internal class SingleWalletWithTokenContentLoader(
private val userWallet: UserWallet.Cold,

View file

@ -2,12 +2,13 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.staking.usecase.StakingApyFlowUseCase
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.account.AccountDependencies
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender
@ -15,11 +16,11 @@ import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarnin
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.account.AccountDependencies
import javax.inject.Inject
// TODO: Refactor
@Suppress("LongParameterList")
@Deprecated("Use SingleWalletWithTokenContentLoaderV2.Factory instead")
@ModelScoped
internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor(
private val stateHolder: WalletStateController,

View file

@ -0,0 +1,50 @@
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender
import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@Suppress("LongParameterList")
internal class SingleWalletWithTokenContentLoaderV2 @AssistedInject constructor(
@Assisted private val userWallet: UserWallet.Cold,
private val singleWalletWithTokenSubscriberFactory: SingleWalletWithTokenSubscriber.Factory,
private val checkWalletWithFundsSubscriberFactory: CheckWalletWithFundsSubscriber.Factory,
private val clickIntents: WalletClickIntents,
private val stateController: WalletStateController,
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender,
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
) : WalletContentLoader(id = userWallet.walletId) {
override fun create(): List<WalletSubscriber> = listOf(
singleWalletWithTokenSubscriberFactory.create(userWallet),
MultiWalletWarningsSubscriber(
userWallet = userWallet,
stateHolder = stateController,
clickIntents = clickIntents,
getMultiWalletWarningsFactory = getMultiWalletWarningsFactory,
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
walletWarningsSingleEventSender = walletWarningsSingleEventSender,
),
WalletDropDownItemsSubscriber(
stateHolder = stateController,
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
clickIntents = clickIntents,
),
checkWalletWithFundsSubscriberFactory.create(userWallet),
)
@AssistedFactory
interface Factory {
fun create(userWallet: UserWallet.Cold): SingleWalletWithTokenContentLoaderV2
}
}

View file

@ -100,6 +100,7 @@ internal class TokenListStateConverter(
appCurrency = appCurrency,
account = account,
onItemClick = onItemClick,
priceChangeLce = this.priceChangeLce,
)
val accountItem = converter.convert(tokenList.totalFiatBalance)
val tokenConverter = tokenStatusConverter(account.accountId)

View file

@ -0,0 +1,146 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.domain.account.models.AccountStatusList
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.core.lce.Lce
import com.tangem.domain.core.utils.getOrElse
import com.tangem.domain.models.PortfolioId
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.tokenlist.TokenList
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.account.AccountDependencies
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenListErrorTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenListTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TokenConverterParams
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import timber.log.Timber
/**
* Basic implementation of [WalletSubscriber] for wallet with accounts.
*
[REDACTED_AUTHOR]
*/
internal abstract class BasicAccountListSubscriber : BasicWalletSubscriber() {
abstract val accountDependencies: AccountDependencies
abstract val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase
abstract val stateController: WalletStateController
abstract val clickIntents: WalletClickIntents
override val singleAccountStatusListSupplier: SingleAccountStatusListSupplier
get() = accountDependencies.singleAccountStatusListSupplier
protected fun getAppCurrencyFlow(): Flow<AppCurrency> {
return getSelectedAppCurrencyUseCase.invokeOrDefault()
.distinctUntilChanged()
}
protected fun updateState(
accountList: AccountStatusList,
appCurrency: AppCurrency,
expandedAccounts: Set<AccountId>,
isAccountMode: Boolean,
yieldSupplyApyMap: Map<String, String> = emptyMap(),
stakingApyMap: Map<String, List<Yield.Validator>> = emptyMap(),
) {
val accountFlattenCurrencies = accountList.flattenCurrencies()
val mainAccount = accountList.mainAccount
when {
!isAccountMode -> {
val isMainAccountEmpty = mainAccount.tokenList.flattenCurrencies().isEmpty()
val maybeTokenList = if (isMainAccountEmpty) {
Lce.Error(TokenListError.EmptyTokens)
} else {
Lce.Content(mainAccount.tokenList)
}
singleAccountTransform(
maybeTokenList = maybeTokenList,
appCurrency = appCurrency,
portfolioId = PortfolioId(mainAccount.accountId),
yieldSupplyApyMap = yieldSupplyApyMap,
stakingApyMap = stakingApyMap,
)
}
isAccountMode -> {
val isAllAccountsEmpty = accountFlattenCurrencies.isEmpty()
if (isAllAccountsEmpty) {
stateController.update(
SetTokenListErrorTransformer(
selectedWallet = userWallet,
error = TokenListError.EmptyTokens,
appCurrency = appCurrency,
),
)
} else {
val convertParams = TokenConverterParams.Account(accountList, expandedAccounts)
updateContent(convertParams, appCurrency, yieldSupplyApyMap, stakingApyMap)
}
}
}
}
private fun singleAccountTransform(
maybeTokenList: Lce<TokenListError, TokenList>,
appCurrency: AppCurrency,
portfolioId: PortfolioId,
yieldSupplyApyMap: Map<String, String> = emptyMap(),
stakingApyMap: Map<String, List<Yield.Validator>> = emptyMap(),
) {
val tokenList = maybeTokenList.getOrElse(
ifLoading = { maybeContent ->
val isRefreshing = stateController.getWalletState(userWallet.walletId)
?.pullToRefreshConfig
?.isRefreshing == true
maybeContent
?.takeIf { !isRefreshing }
?: return
},
ifError = { e ->
Timber.e("Failed to load token list: $e")
stateController.update(
SetTokenListErrorTransformer(
selectedWallet = userWallet,
error = e,
appCurrency = appCurrency,
),
)
return
},
)
updateContent(
params = TokenConverterParams.Wallet(portfolioId, tokenList),
appCurrency = appCurrency,
yieldSupplyApyMap = yieldSupplyApyMap,
stakingApyMap = stakingApyMap,
)
}
private fun updateContent(
params: TokenConverterParams,
appCurrency: AppCurrency,
yieldSupplyApyMap: Map<String, String> = emptyMap(),
stakingApyMap: Map<String, List<Yield.Validator>> = emptyMap(),
) {
stateController.update(
SetTokenListTransformer(
params = params,
userWallet = userWallet,
appCurrency = appCurrency,
clickIntents = clickIntents,
yieldSupplyApyMap = yieldSupplyApyMap,
stakingApyMap = stakingApyMap,
),
)
}
}

View file

@ -0,0 +1,34 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.mapNotNull
/**
* Basic implementation of [WalletSubscriber] for single wallet.
*
[REDACTED_AUTHOR]
*/
internal abstract class BasicSingleWalletSubscriber : BasicWalletSubscriber() {
/** Account ID for the main crypto portfolio of the user wallet */
val accountId: AccountId
get() = AccountId.forMainCryptoPortfolio(userWalletId = userWallet.walletId)
/**
* Provides a flow of the primary [CryptoCurrencyStatus] for the associated user wallet.
*
* @return A [Flow] emitting the primary [CryptoCurrencyStatus] of the wallet, distinct and conflated.
*/
protected fun getPrimaryCurrencyStatusFlow(): Flow<CryptoCurrencyStatus> {
return getMainAccountStatusFlow()
.mapNotNull { accountStatus ->
accountStatus.flattenCurrencies().firstOrNull()
}
.distinctUntilChanged()
.conflate()
}
}

View file

@ -154,9 +154,8 @@ internal abstract class BasicTokenListSubscriber : WalletSubscriber() {
val accountFlattenCurrencies = accountFlattenTokensList
.map { it.flattenCurrencies() }
.flatten()
val mainAccount: AccountStatus.CryptoPortfolio = when (val mainAccount = accountList.mainAccount) {
is AccountStatus.CryptoPortfolio -> mainAccount
}
val mainAccount = accountList.mainAccount
suspend fun singleAccountTransform(maybeTokenList: Lce<TokenListError, TokenList>) =
this.singleAccountTransform(

View file

@ -0,0 +1,56 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.domain.account.models.AccountStatusList
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.wallet.UserWallet
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.mapNotNull
/**
* Basic implementation of [WalletSubscriber] for wallet.
*
[REDACTED_AUTHOR]
*/
internal abstract class BasicWalletSubscriber : WalletSubscriber() {
/** User wallet associated with this subscriber */
abstract val userWallet: UserWallet
/** Supplier to get the account status for the wallet */
abstract val singleAccountStatusListSupplier: SingleAccountStatusListSupplier
/**
* Provides a flow of [AccountStatusList] for the associated user wallet.
*
* @return A [Flow] emitting the [AccountStatusList] of the wallet, distinct and conflated.
*/
protected fun getAccountStatusListFlow(): Flow<AccountStatusList> {
return singleAccountStatusListSupplier(
params = SingleAccountStatusListProducer.Params(userWallet.walletId),
)
.distinctUntilChanged()
.conflate()
}
/**
* Provides a flow of [AccountStatus] for the associated user wallet.
*
* @return A [Flow] emitting the [AccountStatus] of the wallet, distinct and conflated.
*/
protected fun getMainAccountStatusFlow(): Flow<AccountStatus> {
return getAccountStatusListFlow()
.mapNotNull { accountStatusList ->
accountStatusList.accountStatuses.find { accountStatus ->
when (accountStatus) {
is AccountStatus.CryptoPortfolio -> accountStatus.account.isMainAccount
}
}
}
.distinctUntilChanged()
.conflate()
}
}

View file

@ -0,0 +1,42 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
/**
* Subscriber that checks if the wallet has funds and notifies the [WalletWithFundsChecker].
*
* @property userWallet The user wallet.
* @property singleAccountStatusListSupplier Supplier for account status list.
* @property walletWithFundsChecker The checker to notify when funds are detected.
* @property dispatchers Coroutine dispatcher provider.
*
[REDACTED_AUTHOR]
*/
internal class CheckWalletWithFundsSubscriber @AssistedInject constructor(
@Assisted override val userWallet: UserWallet,
override val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val walletWithFundsChecker: WalletWithFundsChecker,
private val dispatchers: CoroutineDispatcherProvider,
) : BasicWalletSubscriber() {
override fun create(coroutineScope: CoroutineScope): Flow<*> {
return getAccountStatusListFlow()
.mapNotNull { it.flattenCurrencies() }
.distinctUntilChanged()
.onEach { walletWithFundsChecker.check(userWalletId = userWallet.walletId, currencies = it) }
.flowOn(dispatchers.default)
}
@AssistedFactory
interface Factory {
fun create(userWallet: UserWallet): CheckWalletWithFundsSubscriber
}
}

View file

@ -20,6 +20,7 @@ import kotlinx.coroutines.flow.*
import timber.log.Timber
import java.math.BigDecimal
@Deprecated("Use PrimaryCurrencySubscriberV2 instead")
internal class PrimaryCurrencySubscriber(
private val userWallet: UserWallet,
private val stateHolder: WalletStateController,

View file

@ -0,0 +1,84 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.common.extensions.isZero
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetPrimaryCurrencyTransformer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.onEach
import java.math.BigDecimal
internal class PrimaryCurrencySubscriberV2(
override val userWallet: UserWallet,
override val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val stateController: WalletStateController,
private val analyticsEventHandler: AnalyticsEventHandler,
) : BasicSingleWalletSubscriber() {
override fun create(coroutineScope: CoroutineScope): Flow<*> {
return combine(
flow = getPrimaryCurrencyStatusFlow(),
flow2 = getSelectedAppCurrencyUseCase.invokeOrDefault(),
transform = ::Pair,
)
.onEach { (status, appCurrency) ->
updateContent(status, appCurrency)
sendAnalyticsEvent(status)
}
}
private fun updateContent(status: CryptoCurrencyStatus, appCurrency: AppCurrency) {
stateController.update(
SetPrimaryCurrencyTransformer(
status = status,
userWallet = userWallet,
appCurrency = appCurrency,
),
)
}
private fun sendAnalyticsEvent(status: CryptoCurrencyStatus) {
val fiatAmount = status.value.fiatAmount
val cardBalanceState = when (status.value) {
is CryptoCurrencyStatus.Loaded,
is CryptoCurrencyStatus.NoAccount,
is CryptoCurrencyStatus.NoAmount,
-> createCardBalanceState(fiatAmount)
is CryptoCurrencyStatus.NoQuote -> AnalyticsParam.CardBalanceState.NoRate
is CryptoCurrencyStatus.Unreachable,
-> AnalyticsParam.CardBalanceState.BlockchainError
is CryptoCurrencyStatus.MissedDerivation,
is CryptoCurrencyStatus.Loading,
is CryptoCurrencyStatus.Custom,
-> null
}
cardBalanceState?.let {
// do not send tokens count for single currency wallet
analyticsEventHandler.send(
event = WalletScreenAnalyticsEvent.Basic.BalanceLoaded(
balance = it,
tokensCount = null,
),
)
}
}
private fun createCardBalanceState(fiatAmount: BigDecimal?): AnalyticsParam.CardBalanceState? {
return when {
fiatAmount == null -> null
fiatAmount.isZero() -> AnalyticsParam.CardBalanceState.Empty
else -> AnalyticsParam.CardBalanceState.Full
}
}
}

View file

@ -7,20 +7,19 @@ import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.account.AccountDependencies
import com.tangem.feature.wallet.presentation.wallet.domain.collectLatest
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetCryptoCurrencyActionsTransformer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
@Deprecated("Use SingleWalletButtonsSubscriberV2 instead")
internal class SingleWalletButtonsSubscriber(
private val userWallet: UserWallet,
private val stateHolder: WalletStateController,
private val clickIntents: WalletClickIntents,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase,
private val accountDependencies: AccountDependencies,
) : WalletSubscriber() {
override fun create(coroutineScope: CoroutineScope): Flow<TokenActionsState> {
@ -31,11 +30,10 @@ internal class SingleWalletButtonsSubscriber(
}
}
.onEach { actions ->
val portfolioId = when (accountDependencies.accountsFeatureToggles.isFeatureEnabled) {
true -> TODO("account") // get main account id for single wallet
false -> PortfolioId(userWallet.walletId)
}
updateContent(actions, portfolioId)
updateContent(
tokenActionsState = actions,
portfolioId = PortfolioId(userWallet.walletId),
)
}
}

View file

@ -0,0 +1,46 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.account.status.usecase.GetCryptoCurrencyActionsUseCaseV2
import com.tangem.domain.models.PortfolioId
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetCryptoCurrencyActionsTransformer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.onEach
internal class SingleWalletButtonsSubscriberV2(
override val userWallet: UserWallet,
override val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val stateController: WalletStateController,
private val clickIntents: WalletClickIntents,
private val getCryptoCurrencyActionsUseCaseV2: GetCryptoCurrencyActionsUseCaseV2,
) : BasicSingleWalletSubscriber() {
@OptIn(ExperimentalCoroutinesApi::class)
override fun create(coroutineScope: CoroutineScope): Flow<TokenActionsState> {
return getPrimaryCurrencyStatusFlow()
.flatMapLatest {
getCryptoCurrencyActionsUseCaseV2(accountId = accountId, currency = it.currency)
}
.onEach {
updateContent(tokenActionsState = it, portfolioId = PortfolioId(userWallet.walletId))
}
}
private fun updateContent(tokenActionsState: TokenActionsState, portfolioId: PortfolioId) {
stateController.update(
SetCryptoCurrencyActionsTransformer(
tokenActionsState = tokenActionsState,
userWallet = userWallet,
clickIntents = clickIntents,
portfolioId = portfolioId,
),
)
}
}

View file

@ -20,6 +20,7 @@ import kotlinx.coroutines.flow.*
import timber.log.Timber
@Suppress("LongParameterList")
@Deprecated("Use SingleWalletExpressStatusesSubscriberV2 instead")
internal class SingleWalletExpressStatusesSubscriber(
private val userWallet: UserWallet,
private val stateHolder: WalletStateController,

View file

@ -0,0 +1,84 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.onramp.GetOnrampTransactionsUseCase
import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase
import com.tangem.domain.onramp.model.cache.OnrampTransaction
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetExpressStatusesTransformer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
@Suppress("LongParameterList")
internal class SingleWalletExpressStatusesSubscriberV2(
override val userWallet: UserWallet,
override val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase,
private val stateController: WalletStateController,
private val clickIntents: WalletClickIntents,
private val analyticsEventHandler: AnalyticsEventHandler,
) : BasicSingleWalletSubscriber() {
@OptIn(ExperimentalCoroutinesApi::class)
override fun create(coroutineScope: CoroutineScope): Flow<*> {
val getOnrampTransactionsFlow = getPrimaryCurrencyStatusFlow().flatMapLatest { currencyStatus ->
getOnrampTransactionsUseCase(
userWalletId = userWallet.walletId,
cryptoCurrencyId = currencyStatus.currency.id,
)
.map { currencyStatus to it }
}
return combine(
flow = getOnrampTransactionsFlow,
flow2 = getSelectedAppCurrencyUseCase.invokeOrDefault(),
transform = ::toTriple,
)
.onEach { (status, maybeTransaction, appCurrency) ->
maybeTransaction.fold(
ifRight = { onrampTxs ->
onrampTxs.clearHiddenTerminal()
stateController.update(
SetExpressStatusesTransformer(
userWalletId = userWallet.walletId,
onrampTxs = onrampTxs,
clickIntents = clickIntents,
cryptoCurrencyStatus = status,
appCurrency = appCurrency,
analyticsEventHandler = analyticsEventHandler,
),
)
},
ifLeft = {
stateController.update(
SetExpressStatusesTransformer(
userWalletId = userWallet.walletId,
onrampTxs = listOf(),
clickIntents = clickIntents,
cryptoCurrencyStatus = status,
appCurrency = appCurrency,
analyticsEventHandler = analyticsEventHandler,
),
)
},
)
}
}
private fun <A, B, C> toTriple(firstPair: Pair<A, B>, second: C): Triple<A, B, C> {
return Triple(firstPair.first, firstPair.second, second)
}
private suspend fun List<OnrampTransaction>.clearHiddenTerminal() {
this
.filter { it.status.isHidden && it.status.isTerminal }
.forEach { onrampRemoveTransactionUseCase(txId = it.txId) }
}
}

View file

@ -19,6 +19,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@Deprecated("Use SingleWalletWithTokenSubscriber instead")
@Suppress("LongParameterList")
internal class SingleWalletWithTokenListSubscriber(
override val userWallet: UserWallet.Cold,

View file

@ -0,0 +1,35 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.account.AccountDependencies
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
internal class SingleWalletWithTokenSubscriber @AssistedInject constructor(
@Assisted override val userWallet: UserWallet.Cold,
override val accountDependencies: AccountDependencies,
override val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
override val stateController: WalletStateController,
override val clickIntents: WalletClickIntents,
) : BasicAccountListSubscriber() {
override fun create(coroutineScope: CoroutineScope): Flow<Unit> = combine(
flow = getAccountStatusListFlow(),
flow2 = getAppCurrencyFlow(),
flow3 = accountDependencies.expandedAccountsHolder.expandedAccounts(userWallet),
flow4 = accountDependencies.isAccountsModeEnabledUseCase(),
transform = ::updateState,
)
@AssistedFactory
interface Factory {
fun create(userWallet: UserWallet.Cold): SingleWalletWithTokenSubscriber
}
}

View file

@ -30,6 +30,7 @@ typealias MaybeTxHistoryCount = Either<TxHistoryStateError, Int>
typealias MaybeTxHistoryItems = Either<TxHistoryListError, Flow<PagingData<TxInfo>>>
@Suppress("LongParameterList")
@Deprecated("Use TxHistorySubscriberV2 instead")
internal class TxHistorySubscriber(
private val userWallet: UserWallet.Cold,
private val isRefresh: Boolean,

View file

@ -0,0 +1,111 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import androidx.paging.PagingData
import androidx.paging.cachedIn
import androidx.paging.map
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.network.TxInfo
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryCountErrorTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryCountTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryItemsErrorTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryItemsTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.TxHistoryItemStateConverter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
@Suppress("LongParameterList")
internal class TxHistorySubscriberV2(
override val userWallet: UserWallet.Cold,
override val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase,
private val isRefresh: Boolean,
private val stateController: WalletStateController,
private val clickIntents: WalletClickIntents,
) : BasicSingleWalletSubscriber() {
override fun create(coroutineScope: CoroutineScope): Flow<PagingData<TxInfo>> {
return flow {
getPrimaryCurrencyStatusFlow().collectLatest { status ->
val maybeTxHistoryItemCount = txHistoryItemsCountUseCase(
userWalletId = userWallet.walletId,
currency = status.currency,
)
setLoadingTxHistoryState(maybeTxHistoryItemCount, status)
maybeTxHistoryItemCount.onRight {
val maybeTxHistoryItems = txHistoryItemsUseCase(
userWalletId = userWallet.walletId,
currency = status.currency,
refresh = isRefresh,
).map { it.cachedIn(coroutineScope) }
setLoadedTxHistoryState(maybeTxHistoryItems)
}
}
}
}
private fun setLoadingTxHistoryState(maybeTxHistoryItemCount: MaybeTxHistoryCount, status: CryptoCurrencyStatus) {
stateController.update(
maybeTxHistoryItemCount.fold(
ifLeft = {
SetTxHistoryCountErrorTransformer(
userWallet = userWallet,
error = it,
pendingTransactions = status.value.pendingTransactions,
clickIntents = clickIntents,
)
},
ifRight = {
SetTxHistoryCountTransformer(
userWalletId = userWallet.walletId,
transactionsCount = it,
clickIntents = clickIntents,
)
},
),
)
}
private fun setLoadedTxHistoryState(maybeTxHistoryItems: MaybeTxHistoryItems) {
stateController.update(
maybeTxHistoryItems.fold(
ifLeft = {
SetTxHistoryItemsErrorTransformer(
userWalletId = userWallet.walletId,
error = it,
clickIntents = clickIntents,
)
},
ifRight = { itemsFlow ->
val blockchain = userWallet.scanResponse.cardTypesResolver.getBlockchain()
val itemConverter = TxHistoryItemStateConverter(
symbol = blockchain.currency,
decimals = blockchain.decimals(),
clickIntents = clickIntents,
)
SetTxHistoryItemsTransformer(
userWallet = userWallet,
flow = itemsFlow.map { items ->
items.map(itemConverter::convert)
},
clickIntents = clickIntents,
)
},
),
)
}
}

View file

@ -5,7 +5,9 @@ import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetWalletCardDropDownItemsTransformer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.onEach
internal class WalletDropDownItemsSubscriber(
private val stateHolder: WalletStateController,
@ -13,18 +15,15 @@ internal class WalletDropDownItemsSubscriber(
private val clickIntents: WalletClickIntents,
) : WalletSubscriber() {
override fun create(coroutineScope: CoroutineScope): Flow<*> {
return flow<Any> {
shouldSaveUserWalletsUseCase.invoke()
.distinctUntilChanged()
.onEach {
stateHolder.update(
SetWalletCardDropDownItemsTransformer(
dropdownEnabled = it,
clickIntents = clickIntents,
),
)
}
.launchIn(coroutineScope)
}
return shouldSaveUserWalletsUseCase.invoke()
.distinctUntilChanged()
.onEach {
stateHolder.update(
SetWalletCardDropDownItemsTransformer(
dropdownEnabled = it,
clickIntents = clickIntents,
),
)
}
}
}

View file

@ -69,6 +69,7 @@ import com.tangem.core.ui.utils.moveTo
import com.tangem.core.ui.utils.toPx
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.accountScreenState
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.accountScreenWithEmptyTokensState
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.walletScreenState
import com.tangem.feature.wallet.presentation.wallet.state.model.*
import com.tangem.feature.wallet.presentation.wallet.state.model.holder.TxHistoryStateHolder
@ -142,6 +143,7 @@ private fun WalletContent(
*/
val selectedWalletIndex by remember(state.selectedWalletIndex) { mutableIntStateOf(state.selectedWalletIndex) }
val selectedWallet = state.wallets.getOrElse(selectedWalletIndex) { state.wallets[state.selectedWalletIndex] }
val selectedWalletChanged = rememberChangedOnce(selectedWalletIndex)
val listState = rememberLazyListState()
@ -239,6 +241,7 @@ private fun WalletContent(
contentItems(
state = selectedWallet,
selectedWalletChanged = selectedWalletChanged,
txHistoryItems = txHistoryItems,
isBalanceHidden = state.isHidingMode,
modifier = movableItemModifier,
@ -289,6 +292,14 @@ private fun WalletContent(
)
}
@Composable
private fun rememberChangedOnce(selectedWalletIndex: Int): Boolean {
var prev by remember { mutableIntStateOf(selectedWalletIndex) }
val changed = prev != selectedWalletIndex
SideEffect { prev = selectedWalletIndex }
return changed
}
@Suppress("LongParameterList", "LongMethod", "CyclomaticComplexMethod")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@ -743,6 +754,7 @@ private class WalletScreenPreviewProvider : PreviewParameterProvider<WalletScree
walletScreenState,
walletScreenState.copy(selectedWalletIndex = 1),
accountScreenState.copy(selectedWalletIndex = 1),
accountScreenWithEmptyTokensState.copy(selectedWalletIndex = 1),
)
}
// endregion

View file

@ -22,10 +22,11 @@ internal fun LazyListScope.contentItems(
txHistoryItems: LazyPagingItems<TxHistoryState.TxHistoryItemState>?,
isBalanceHidden: Boolean,
modifier: Modifier = Modifier,
selectedWalletChanged: Boolean,
) {
when (state) {
is WalletState.MultiCurrency -> {
tokensListItems(state.tokensListState, modifier, isBalanceHidden)
tokensListItems(state.tokensListState, modifier, isBalanceHidden, selectedWalletChanged)
}
is WalletState.SingleCurrency -> {
txHistoryItems(state.txHistoryState, txHistoryItems, isBalanceHidden, modifier)

View file

@ -27,6 +27,7 @@ internal fun LazyListScope.portfolioContentItems(
items: ImmutableList<TokensListItemUM.Portfolio>,
modifier: Modifier = Modifier,
isBalanceHidden: Boolean,
selectedWalletChanged: Boolean,
) {
items.forEachIndexed { index, item ->
portfolioTokensList(
@ -34,6 +35,7 @@ internal fun LazyListScope.portfolioContentItems(
modifier = modifier,
portfolioIndex = index,
isBalanceHidden = isBalanceHidden,
selectedWalletChanged = selectedWalletChanged,
)
}
}
@ -43,6 +45,7 @@ internal fun LazyListScope.portfolioTokensList(
modifier: Modifier,
portfolioIndex: Int,
isBalanceHidden: Boolean,
selectedWalletChanged: Boolean,
) {
val tokens = portfolio.tokens
val isExpanded = portfolio.isExpanded
@ -52,8 +55,28 @@ internal fun LazyListScope.portfolioTokensList(
modifier = modifier,
portfolioIndex = portfolioIndex,
isBalanceHidden = isBalanceHidden,
selectedWalletChanged = selectedWalletChanged,
)
if (!isExpanded) return
if (tokens.isEmpty()) {
item(
key = "$NON_CONTENT_TOKENS_LIST_KEY account-${portfolio.id}",
contentType = "$NON_CONTENT_TOKENS_LIST_KEY account-${portfolio.id}",
) {
NonContentItemContent(
modifier = Modifier
.animateItem()
.roundedShapeItemDecoration(
radius = TangemTheme.dimens.radius14,
currentIndex = 1,
lastIndex = 1,
backgroundColor = TangemTheme.colors.background.primary,
)
.padding(vertical = TangemTheme.dimens.spacing28),
)
}
return
}
itemsIndexed(
items = tokens,
key = { _, item -> item.id },
@ -63,13 +86,14 @@ internal fun LazyListScope.portfolioTokensList(
val lastIndex = tokens.lastIndex.inc()
val isPreview = LocalInspectionMode.current
val appear = remember {
MutableTransitionState(isPreview).apply { targetState = true }
MutableTransitionState(selectedWalletChanged || isPreview).apply { targetState = true }
}
SlideInItemVisibility(
modifier = modifier
.testModifier(indexWithHeader)
.animateItem()
.roundedShapeItemDecoration(
radius = TangemTheme.dimens.radius14,
currentIndex = indexWithHeader,
lastIndex = lastIndex,
backgroundColor = TangemTheme.colors.background.primary,
@ -92,10 +116,17 @@ private fun LazyListScope.portfolioItem(
modifier: Modifier,
portfolioIndex: Int,
isBalanceHidden: Boolean,
selectedWalletChanged: Boolean,
) {
val tokens = portfolio.tokens
val isExpanded = portfolio.isExpanded
val lastIndex = when {
isExpanded && tokens.isEmpty() -> 1
isExpanded -> tokens.lastIndex.inc()
else -> 0
}
item(
key = "account-${portfolio.id}-isExpanded$isExpanded",
contentType = "account-isExpanded$isExpanded",
@ -105,27 +136,24 @@ private fun LazyListScope.portfolioItem(
.animateItem()
.roundedShapeItemDecoration(
currentIndex = 0,
lastIndex = if (isExpanded) tokens.lastIndex.inc() else 0,
radius = TangemTheme.dimens.radius14,
lastIndex = lastIndex,
backgroundColor = TangemTheme.colors.background.primary,
)
val isPreview = LocalInspectionMode.current
val appear = remember {
MutableTransitionState(isPreview).apply { targetState = true }
MutableTransitionState(selectedWalletChanged || isPreview || tokens.isEmpty())
.apply { targetState = true }
}
if (isExpanded) {
SlideInItemVisibility(
modifier = anchorModifier,
visibleState = appear,
) {
val modifier = if (portfolio.tokens.isEmpty()) {
Modifier.padding(vertical = 8.dp)
} else {
Modifier.padding(top = 8.dp)
}
PortfolioListItem(
state = portfolio,
isBalanceHidden = isBalanceHidden,
modifier = modifier,
modifier = Modifier.padding(vertical = 8.dp),
)
}
} else {

View file

@ -8,6 +8,7 @@ import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
@ -25,7 +26,7 @@ import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState
import kotlinx.collections.immutable.ImmutableList
private const val NON_CONTENT_TOKENS_LIST_KEY = "NON_CONTENT_TOKENS_LIST"
internal const val NON_CONTENT_TOKENS_LIST_KEY = "NON_CONTENT_TOKENS_LIST"
/**
* LazyList extension for [WalletTokensListState]
@ -39,12 +40,14 @@ internal fun LazyListScope.tokensListItems(
state: WalletTokensListState,
modifier: Modifier = Modifier,
isBalanceHidden: Boolean,
selectedWalletChanged: Boolean,
) {
when (state) {
is WalletTokensListState.ContentState.PortfolioContent -> portfolioContentItems(
items = state.items,
isBalanceHidden = isBalanceHidden,
modifier = modifier,
selectedWalletChanged = selectedWalletChanged,
)
is WalletTokensListState.ContentState.Content,
is WalletTokensListState.ContentState.Loading,
@ -89,27 +92,34 @@ private fun LazyListScope.nonContentItem(modifier: Modifier = Modifier) {
key = NON_CONTENT_TOKENS_LIST_KEY,
contentType = NON_CONTENT_TOKENS_LIST_KEY,
) {
Column(
NonContentItemContent(
modifier = modifier
.animateItem(fadeInSpec = null, fadeOutSpec = null)
.padding(top = TangemTheme.dimens.spacing96),
verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing16),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
painter = painterResource(id = R.drawable.ic_empty_64),
contentDescription = null,
modifier = Modifier.size(size = TangemTheme.dimens.size64),
tint = TangemTheme.colors.icon.inactive,
)
)
}
}
Text(
text = stringResourceSafe(id = R.string.main_empty_tokens_list_message),
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing48),
color = TangemTheme.colors.text.tertiary,
textAlign = TextAlign.Center,
style = TangemTheme.typography.caption2,
)
}
@Composable
internal fun NonContentItemContent(modifier: Modifier = Modifier) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing16),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
painter = painterResource(id = R.drawable.ic_empty_64),
contentDescription = null,
modifier = Modifier.size(size = TangemTheme.dimens.size64),
tint = TangemTheme.colors.icon.inactive,
)
Text(
text = stringResourceSafe(id = R.string.main_empty_tokens_list_message),
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing48),
color = TangemTheme.colors.text.tertiary,
textAlign = TextAlign.Center,
style = TangemTheme.typography.caption2,
)
}
}

View file

@ -1,6 +1,5 @@
package com.tangem.feature.wallet.utils
import arrow.core.Either
import com.tangem.common.ui.userwallet.converter.ArtworkUMConverter
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.ui.components.artwork.ArtworkUM
@ -47,12 +46,11 @@ class DefaultUserWalletImageFetcher @Inject constructor(
override fun walletImage(walletId: UserWalletId, size: ArtworkSize): Flow<UserWalletItemUM.ImageState> =
getUserWalletUseCase.invokeFlow(walletId)
.transform { either ->
if (either.isLeft()) {
emit(UserWalletItemUM.ImageState.Loading)
} else if (either is Either.Right) {
emitAll(walletImage(either.value, size))
}
.transform {
it.fold(
ifLeft = { emit(UserWalletItemUM.ImageState.Loading) },
ifRight = { wallet -> emitAll(walletImage(wallet, size)) },
)
}
.distinctUntilChanged()

View file

@ -6,6 +6,8 @@ import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.account.status.usecase.GetWalletTotalBalanceUseCaseV2
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.error.SelectedAppCurrencyError
import com.tangem.domain.appcurrency.model.AppCurrency
@ -37,7 +39,9 @@ import kotlinx.coroutines.flow.*
@Suppress("LongParameterList")
internal class DefaultUserWalletsFetcher @AssistedInject constructor(
getWalletsUseCase: GetWalletsUseCase,
private val accountsFeatureToggles: AccountsFeatureToggles,
private val getWalletTotalBalanceUseCase: GetWalletTotalBalanceUseCase,
private val getWalletTotalBalanceUseCaseV2: GetWalletTotalBalanceUseCaseV2,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
@Assisted private val onWalletClick: (UserWalletId) -> Unit,
@ -97,7 +101,11 @@ internal class DefaultUserWalletsFetcher @AssistedInject constructor(
// We should not load balances in auth mode
flowOf(Lce.Loading(walletIds.associateWith { TotalFiatBalance.Loading }))
} else {
getWalletTotalBalanceUseCase(walletIds).distinctUntilChanged()
if (accountsFeatureToggles.isFeatureEnabled) {
getWalletTotalBalanceUseCaseV2(userWalletIds = walletIds)
} else {
getWalletTotalBalanceUseCase(walletIds).distinctUntilChanged()
}
}
}