Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-02 16:53:46 +03:00
parent 09f1669029
commit 81ef606a43
21 changed files with 283 additions and 157 deletions

View file

@ -0,0 +1,31 @@
package com.tangem.common.ui.account
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.components.tokenlist.state.PortfolioItemContentUM
import com.tangem.core.ui.components.tokenlist.state.PortfolioTokensListItemUM
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.ImmutableList
class TokensListPortfolioItemConverter(
val tokenItemUM: TokenItemState,
val isExpanded: Boolean,
val isCollapsable: Boolean,
val tokens: ImmutableList<PortfolioTokensListItemUM>,
val onEmptyAction: PortfolioItemContentUM.Empty.Action? = null,
) : Converter<Unit, TokensListItemUM.Portfolio> {
override fun convert(value: Unit): TokensListItemUM.Portfolio {
val content = if (tokens.isEmpty()) {
PortfolioItemContentUM.Empty(onEmptyAction)
} else {
PortfolioItemContentUM.Tokens(tokens)
}
return TokensListItemUM.Portfolio(
tokenItemUM = tokenItemUM,
isExpanded = isExpanded,
isCollapsable = isCollapsable,
content = content,
)
}
}

View file

@ -5,6 +5,7 @@ import com.tangem.core.ui.components.fields.entity.SearchBarUM
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.extensions.TextReference
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
/** Tokens list item state */
@Immutable
@ -45,15 +46,34 @@ sealed interface TokensListItemUM {
val tokenItemUM: TokenItemState,
val isExpanded: Boolean,
val isCollapsable: Boolean,
val tokens: ImmutableList<PortfolioTokensListItemUM>,
val content: PortfolioItemContentUM,
) : TokensListItemUM {
override val id: String = tokenItemUM.id
val tokens: ImmutableList<PortfolioTokensListItemUM>
get() = when (content) {
is PortfolioItemContentUM.Tokens -> content.tokens
is PortfolioItemContentUM.Empty -> persistentListOf()
}
}
data class Text(override val id: Any, val text: TextReference) : TokensListItemUM
}
@Immutable
sealed interface PortfolioTokensListItemUM {
/** Unique ID */
val id: Any
}
@Immutable
sealed interface PortfolioItemContentUM {
data class Tokens(val tokens: ImmutableList<PortfolioTokensListItemUM>) : PortfolioItemContentUM
data class Empty(val action: Action? = null) : PortfolioItemContentUM {
data class Action(
val text: TextReference,
val onClick: () -> Unit,
)
}
}

View file

@ -1,6 +1,7 @@
package com.tangem.features.onramp.swap.availablepairs.entity.converters
import com.tangem.common.ui.account.AccountCryptoPortfolioItemStateConverter
import com.tangem.common.ui.account.TokensListPortfolioItemConverter
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.TotalFiatBalance
@ -15,7 +16,7 @@ internal class LoadingAccountTokenItemConverter(
override fun convert(value: AccountStatus.CryptoPortfolio): TokensListItemUM.Portfolio {
val (account, currencies) = value
return TokensListItemUM.Portfolio(
return TokensListPortfolioItemConverter(
tokenItemUM = AccountCryptoPortfolioItemStateConverter(
appCurrency = appCurrency,
account = account,
@ -26,6 +27,6 @@ internal class LoadingAccountTokenItemConverter(
tokens = currencies.flattenCurrencies()
.map { LoadingTokenListItemConverter.convert(it.currency) }
.toPersistentList(),
)
).convert(Unit)
}
}

View file

@ -1,6 +1,7 @@
package com.tangem.features.onramp.swap.availablepairs.entity.transformers
import com.tangem.common.ui.account.AccountCryptoPortfolioItemStateConverter
import com.tangem.common.ui.account.TokensListPortfolioItemConverter
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
import com.tangem.core.ui.extensions.TextReference
@ -34,7 +35,7 @@ internal class SetNoAvailablePairsTransformerV2(
tokensListData = if (isAccountsMode) {
TokenListUMData.AccountList(
tokensList = accountList.map { (account, cryptoCurrencies) ->
TokensListItemUM.Portfolio(
TokensListPortfolioItemConverter(
tokenItemUM = AccountCryptoPortfolioItemStateConverter(
appCurrency = appCurrency,
account = account,
@ -45,7 +46,7 @@ internal class SetNoAvailablePairsTransformerV2(
tokens = unavailableConverter.convertList(cryptoCurrencies)
.map(TokensListItemUM::Token)
.toPersistentList(),
)
).convert(Unit)
}.toPersistentList(),
totalTokensCount = totalTokensCount,
)

View file

@ -1,6 +1,7 @@
package com.tangem.features.onramp.tokenlist.entity.transformer
import com.tangem.common.ui.account.AccountCryptoPortfolioItemStateConverter
import com.tangem.common.ui.account.TokensListPortfolioItemConverter
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
import com.tangem.core.ui.extensions.TextReference
@ -25,7 +26,7 @@ internal class UpdateAccountTokenItemConverter(
.createUnavailableItemConverterV2(appCurrency = appCurrency, unavailableErrorText = unavailableErrorText)
override fun convert(value: AccountAvailabilityUM): TokensListItemUM.Portfolio {
return TokensListItemUM.Portfolio(
return TokensListPortfolioItemConverter(
tokenItemUM = AccountCryptoPortfolioItemStateConverter(
appCurrency = appCurrency,
account = value.account,
@ -40,6 +41,6 @@ internal class UpdateAccountTokenItemConverter(
unavailableConverter.convert(status)
}
}.map(TokensListItemUM::Token).toPersistentList(),
)
).convert(Unit)
}
}

View file

@ -3,6 +3,7 @@ package com.tangem.feature.swap.converters
import com.tangem.common.getTotalCryptoAmount
import com.tangem.common.getTotalFiatAmount
import com.tangem.common.ui.account.AccountCryptoPortfolioItemStateConverter
import com.tangem.common.ui.account.TokensListPortfolioItemConverter
import com.tangem.common.ui.tokens.TokenItemStateConverter
import com.tangem.common.ui.tokens.TokenItemStateConverter.Companion.isFlickering
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
@ -27,7 +28,7 @@ internal class AccountTokenItemConverter(
) : Converter<AccountSwapAvailability, TokensListItemUM.Portfolio> {
override fun convert(value: AccountSwapAvailability): TokensListItemUM.Portfolio {
return TokensListItemUM.Portfolio(
return TokensListPortfolioItemConverter(
tokenItemUM = AccountCryptoPortfolioItemStateConverter(
appCurrency = appCurrency,
account = value.account,
@ -39,7 +40,7 @@ internal class AccountTokenItemConverter(
createAvailableItemConverter()
.convert(accountSwapCurrency.cryptoCurrencyStatus)
}.map(TokensListItemUM::Token).toPersistentList(),
)
).convert(Unit)
}
fun createAvailableItemConverter(): TokenItemStateConverter {

View file

@ -9,6 +9,7 @@ import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.staking.StakingBalance
@ -28,7 +29,11 @@ import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAn
import com.tangem.feature.wallet.presentation.wallet.domain.OnrampStatusFactory
import com.tangem.feature.wallet.presentation.wallet.domain.unwrap
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.model.*
import com.tangem.feature.wallet.presentation.wallet.state.model.ActionsBottomSheetConfig
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertUM
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNFTItemUM
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBottomSheetTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.OpenBottomSheetTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.MultiWalletCurrencyActionsConverter
@ -48,16 +53,11 @@ internal interface WalletContentClickIntents {
fun onDismissMarketsTooltip()
fun onTokenItemClick(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus)
fun onTokenItemClick(accountId: AccountId, currencyStatus: CryptoCurrencyStatus)
fun onTokenItemLongClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onTokenItemLongClick(accountId: AccountId, cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onApyLabelClick(
userWalletId: UserWalletId,
currencyStatus: CryptoCurrencyStatus,
apySource: ApySource,
apy: String,
)
fun onApyLabelClick(accountId: AccountId, currencyStatus: CryptoCurrencyStatus, apySource: ApySource, apy: String)
fun onYieldPromoCloseClick()
@ -69,6 +69,8 @@ internal interface WalletContentClickIntents {
fun onAccountCollapseClick(account: Account)
fun onManageTokensClick(accountId: AccountId)
fun onTransactionClick(txHash: String)
fun onDissmissBottomSheet()
@ -119,12 +121,13 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
}
}
override fun onTokenItemClick(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus) {
router.openTokenDetails(userWalletId, currencyStatus)
override fun onTokenItemClick(accountId: AccountId, currencyStatus: CryptoCurrencyStatus) {
router.openTokenDetails(accountId.userWalletId, currencyStatus)
}
override fun onTokenItemLongClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus) {
override fun onTokenItemLongClick(accountId: AccountId, cryptoCurrencyStatus: CryptoCurrencyStatus) {
modelScope.launch(dispatchers.main) {
val userWalletId = accountId.userWalletId
val userWallet = getUserWalletUseCase(userWalletId).getOrElse {
Timber.e(
"""
@ -140,13 +143,13 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
getCryptoCurrencyActionsUseCase(userWallet = userWallet, cryptoCurrencyStatus = cryptoCurrencyStatus)
.take(count = 1)
.collectLatest {
showActionsBottomSheet(it, userWallet)
showActionsBottomSheet(it, userWallet, accountId)
}
}
}
override fun onApyLabelClick(
userWalletId: UserWalletId,
accountId: AccountId,
currencyStatus: CryptoCurrencyStatus,
apySource: ApySource,
apy: String,
@ -161,9 +164,13 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
sendApyLabelClickAnalytics(navigationAction, currencyStatus)
when (navigationAction) {
is NavigationAction.Staking -> router.openTokenDetails(userWalletId, currencyStatus, navigationAction)
is NavigationAction.Staking -> router.openTokenDetails(
accountId.userWalletId,
currencyStatus,
navigationAction,
)
is NavigationAction.YieldSupply -> openYieldSupply(
userWalletId = userWalletId,
userWalletId = accountId.userWalletId,
cryptoCurrencyStatus = currencyStatus,
apy = apy,
)
@ -209,6 +216,10 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
accountDependencies.expandedAccountsHolder.collapseAccount(account.accountId)
}
override fun onManageTokensClick(accountId: AccountId) {
router.openManageTokensScreen(accountId)
}
private fun openYieldSupply(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus, apy: String) {
router.openYieldSupplyEntryScreen(
userWalletId = userWalletId,
@ -248,11 +259,16 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
analyticsEventHandler.send(event)
}
private fun showActionsBottomSheet(tokenActionsState: TokenActionsState, userWallet: UserWallet) {
private fun showActionsBottomSheet(
tokenActionsState: TokenActionsState,
userWallet: UserWallet,
accountId: AccountId,
) {
stateHolder.showBottomSheet(
ActionsBottomSheetConfig(
actions = MultiWalletCurrencyActionsConverter(
userWallet = userWallet,
accountId = accountId,
clickIntents = currencyActionsClickIntents,
).convert(tokenActionsState),
),

View file

@ -22,7 +22,6 @@ import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.haptic.TangemHapticEffect
import com.tangem.core.ui.haptic.VibratorHapticManager
import com.tangem.core.ui.message.DialogMessage
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.extenstions.unwrap
@ -32,6 +31,7 @@ import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.markets.TokenMarketParams
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.network.NetworkAddress
@ -68,14 +68,13 @@ import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch
import timber.log.Timber
import java.math.BigDecimal
import javax.inject.Inject
interface WalletCurrencyActionsClickIntents {
fun onSendClick(
userWalletId: UserWalletId,
accountId: AccountId,
cryptoCurrencyStatus: CryptoCurrencyStatus,
unavailabilityReason: ScenarioUnavailabilityReason,
)
@ -83,32 +82,28 @@ interface WalletCurrencyActionsClickIntents {
fun onSellClick(cryptoCurrencyStatus: CryptoCurrencyStatus, unavailabilityReason: ScenarioUnavailabilityReason)
fun onBuyClick(
userWalletId: UserWalletId,
accountId: AccountId,
cryptoCurrencyStatus: CryptoCurrencyStatus,
unavailabilityReason: ScenarioUnavailabilityReason,
)
fun onSwapClick(
cryptoCurrencyStatus: CryptoCurrencyStatus,
userWalletId: UserWalletId,
accountId: AccountId,
unavailabilityReason: ScenarioUnavailabilityReason,
)
fun onReceiveClick(
userWalletId: UserWalletId,
cryptoCurrencyStatus: CryptoCurrencyStatus,
event: AnalyticsEvent? = null,
)
fun onReceiveClick(accountId: AccountId, cryptoCurrencyStatus: CryptoCurrencyStatus, event: AnalyticsEvent? = null)
fun onStakeClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus, option: StakingOption?)
fun onStakeClick(accountId: AccountId, cryptoCurrencyStatus: CryptoCurrencyStatus, option: StakingOption?)
fun onCopyAddressLongClick(cryptoCurrencyStatus: CryptoCurrencyStatus): TextReference?
fun onCopyAddressClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onCopyAddressClick(accountId: AccountId, cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onHideTokensClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onHideTokensClick(accountId: AccountId, cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onPerformHideToken(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onPerformHideToken(accountId: AccountId, cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onExploreClick()
@ -146,13 +141,12 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
private val needShowYieldSupplyDepositedWarningUseCase: NeedShowYieldSupplyDepositedWarningUseCase,
private val saveViewedYieldSupplyWarningUseCase: SaveViewedYieldSupplyWarningUseCase,
private val isCryptoCurrencyCoinCouldHide: IsCryptoCurrencyCoinCouldHideUseCase,
private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase,
private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
private val uiMessageSender: UiMessageSender,
) : BaseWalletClickIntents(), WalletCurrencyActionsClickIntents {
override fun onSendClick(
userWalletId: UserWalletId,
accountId: AccountId,
cryptoCurrencyStatus: CryptoCurrencyStatus,
unavailabilityReason: ScenarioUnavailabilityReason,
) {
@ -176,18 +170,18 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
modelScope.launch {
saveViewedYieldSupplyWarningUseCase(cryptoCurrencyStatus.currency.name)
stateHolder.hideBottomSheet()
navigateToSend(cryptoCurrencyStatus, userWalletId)
navigateToSend(cryptoCurrencyStatus, accountId.userWalletId)
}
},
)
} else {
navigateToSend(cryptoCurrencyStatus, userWalletId)
navigateToSend(cryptoCurrencyStatus, accountId.userWalletId)
}
}
}
override fun onReceiveClick(
userWalletId: UserWalletId,
accountId: AccountId,
cryptoCurrencyStatus: CryptoCurrencyStatus,
event: AnalyticsEvent?,
) {
@ -240,7 +234,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
return resourceReference(R.string.wallet_notification_address_copied)
}
override fun onCopyAddressClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus) {
override fun onCopyAddressClick(accountId: AccountId, cryptoCurrencyStatus: CryptoCurrencyStatus) {
analyticsEventHandler.send(
event = TokenReceiveNewAnalyticsEvent.ButtonCopyAddress(
token = cryptoCurrencyStatus.currency.symbol,
@ -251,10 +245,10 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
modelScope.launch(dispatchers.main) {
walletManagersFacade.getDefaultAddress(
userWalletId = userWalletId,
userWalletId = accountId.userWalletId,
network = cryptoCurrencyStatus.currency.network,
)?.let { address ->
stateHolder.update(CloseBottomSheetTransformer(userWalletId = userWalletId))
stateHolder.update(CloseBottomSheetTransformer(userWalletId = accountId.userWalletId))
clipboardManager.setText(text = address, isSensitive = true)
walletEventSender.send(event = WalletEvent.CopyAddress)
@ -262,7 +256,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
}
}
override fun onHideTokensClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus) {
override fun onHideTokensClick(accountId: AccountId, cryptoCurrencyStatus: CryptoCurrencyStatus) {
analyticsEventHandler.send(
event = TokenScreenAnalyticsEvent.ButtonRemoveToken(cryptoCurrencyStatus.currency.symbol),
)
@ -270,34 +264,22 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
modelScope.launch(dispatchers.main) {
val currency = cryptoCurrencyStatus.currency
val isCryptoCurrencyCoinCouldHide = currency is CryptoCurrency.Coin &&
!isCryptoCurrencyCoinCouldHide(userWalletId = userWalletId, cryptoCurrencyCoin = currency)
!isCryptoCurrencyCoinCouldHide(userWalletId = accountId.userWalletId, cryptoCurrencyCoin = currency)
if (isCryptoCurrencyCoinCouldHide) {
uiMessageSender.send(WalletAlertUM.unableHideToken(cryptoCurrency = cryptoCurrencyStatus.currency))
} else {
uiMessageSender.send(
WalletAlertUM.hideTokenConfirm(cryptoCurrency = cryptoCurrencyStatus.currency) {
onPerformHideToken(userWalletId, cryptoCurrencyStatus)
onPerformHideToken(accountId, cryptoCurrencyStatus)
},
)
}
}
}
override fun onPerformHideToken(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus) {
override fun onPerformHideToken(accountId: AccountId, cryptoCurrencyStatus: CryptoCurrencyStatus) {
modelScope.launch(dispatchers.io) {
val accountId = getAccountCurrencyStatusUseCase.invokeSync(
userWalletId = userWalletId,
currency = cryptoCurrencyStatus.currency,
)
.map { it.account.accountId }
.getOrNull()
if (accountId == null) {
Timber.e("Account ID is null, cannot hide currency ${cryptoCurrencyStatus.currency.id}")
return@launch
}
manageCryptoCurrenciesUseCase(accountId = accountId, remove = cryptoCurrencyStatus.currency)
.fold(
ifLeft = {
@ -306,7 +288,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
)
},
ifRight = {
stateHolder.update(CloseBottomSheetTransformer(userWalletId = userWalletId))
stateHolder.update(CloseBottomSheetTransformer(userWalletId = accountId.userWalletId))
},
)
}
@ -340,7 +322,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
}
override fun onBuyClick(
userWalletId: UserWalletId,
accountId: AccountId,
cryptoCurrencyStatus: CryptoCurrencyStatus,
unavailabilityReason: ScenarioUnavailabilityReason,
) {
@ -356,7 +338,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
appRouter.push(
AppRoute.Onramp(
userWalletId = userWalletId,
userWalletId = accountId.userWalletId,
currency = cryptoCurrencyStatus.currency,
source = OnrampSource.TOKEN_LONG_TAP,
),
@ -365,7 +347,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
override fun onSwapClick(
cryptoCurrencyStatus: CryptoCurrencyStatus,
userWalletId: UserWalletId,
accountId: AccountId,
unavailabilityReason: ScenarioUnavailabilityReason,
) {
analyticsEventHandler.send(
@ -388,12 +370,12 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
modelScope.launch {
saveViewedYieldSupplyWarningUseCase(cryptoCurrencyStatus.currency.name)
stateHolder.hideBottomSheet()
navigateToSwap(cryptoCurrencyStatus, userWalletId)
navigateToSwap(cryptoCurrencyStatus, accountId.userWalletId)
}
},
)
} else {
navigateToSwap(cryptoCurrencyStatus, userWalletId)
navigateToSwap(cryptoCurrencyStatus, accountId.userWalletId)
}
}
}
@ -435,11 +417,11 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
}
override fun onStakeClick(
userWalletId: UserWalletId,
accountId: AccountId,
cryptoCurrencyStatus: CryptoCurrencyStatus,
option: StakingOption?,
) {
stateHolder.update(CloseBottomSheetTransformer(userWalletId = userWalletId))
stateHolder.update(CloseBottomSheetTransformer(userWalletId = accountId.userWalletId))
val integrationId = option?.integrationId ?: return
@ -448,7 +430,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
appRouter.push(
AppRoute.Staking(
userWalletId = userWalletId,
userWalletId = accountId.userWalletId,
cryptoCurrency = cryptoCurrency,
integrationId = integrationId,
),

View file

@ -6,6 +6,7 @@ import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState
import com.tangem.core.ui.components.token.AccountItemPreviewData
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.components.tokenlist.state.PortfolioItemContentUM
import com.tangem.core.ui.components.tokenlist.state.PortfolioTokensListItemUM
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
import com.tangem.core.ui.event.consumedEvent
@ -97,14 +98,20 @@ internal object WalletScreenPreviewDataLegacy {
private val portfolioContentState = WalletTokensListState.ContentState.PortfolioContent(
items = persistentListOf(
TokensListItemUM.Portfolio(
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>().toPersistentList(),
content = PortfolioItemContentUM.Tokens(
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>()
.toPersistentList(),
),
isExpanded = false,
isCollapsable = true,
tokenItemUM = AccountItemPreviewData.accountItem
.copy(iconState = AccountItemPreviewData.accountLetterIcon),
),
TokensListItemUM.Portfolio(
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>().toPersistentList(),
content = PortfolioItemContentUM.Tokens(
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>()
.toPersistentList(),
),
isExpanded = true,
isCollapsable = true,
tokenItemUM = AccountItemPreviewData.accountItem,
@ -119,14 +126,22 @@ internal object WalletScreenPreviewDataLegacy {
private val emptyPortfolioContentState = WalletTokensListState.ContentState.PortfolioContent(
items = persistentListOf(
TokensListItemUM.Portfolio(
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>().toPersistentList(),
content = PortfolioItemContentUM.Tokens(
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>()
.toPersistentList(),
),
isExpanded = false,
isCollapsable = true,
tokenItemUM = AccountItemPreviewData.accountItem
.copy(iconState = AccountItemPreviewData.accountLetterIcon),
),
TokensListItemUM.Portfolio(
tokens = persistentListOf(),
content = PortfolioItemContentUM.Empty(
action = PortfolioItemContentUM.Empty.Action(
text = stringReference("Manage tokens"),
onClick = {},
),
),
isExpanded = true,
isCollapsable = true,
tokenItemUM = AccountItemPreviewData.accountItem,

View file

@ -6,7 +6,9 @@ import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.domain.models.PortfolioId
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.scan.ScanResponse
@ -50,6 +52,14 @@ internal class DefaultWalletRouter @Inject constructor(
)
}
override fun openManageTokensScreen(accountId: AccountId) {
val route = AppRoute.ManageTokens(
source = AppRoute.ManageTokens.Source.ACCOUNT,
portfolioId = PortfolioId(accountId),
)
router.push(route)
}
override fun openOnboardingScreen(scanResponse: ScanResponse, continueBackup: Boolean) {
router.push(
AppRoute.Onboarding(

View file

@ -4,6 +4,7 @@ import androidx.compose.runtime.Stable
import com.arkivanov.decompose.router.slot.SlotNavigation
import com.tangem.common.routing.AppRoute
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.scan.ScanResponse
@ -37,6 +38,9 @@ internal interface InnerWalletRouter {
/** Open details screen */
fun openDetailsScreen(selectedWalletId: UserWalletId)
/** Open manage tokens screen */
fun openManageTokensScreen(accountId: AccountId)
/** Open onboarding screen */
fun openOnboardingScreen(scanResponse: ScanResponse, continueBackup: Boolean = false)

View file

@ -1,7 +1,7 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.PortfolioId
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
import com.tangem.domain.tokens.model.TokenActionsState
@ -16,7 +16,7 @@ import timber.log.Timber
internal class SetCryptoCurrencyActionsTransformer(
private val tokenActionsState: TokenActionsState,
private val userWallet: UserWallet,
private val portfolioId: PortfolioId,
private val accountId: AccountId,
private val clickIntents: WalletClickIntents,
) : WalletStateTransformer(userWallet.walletId) {
@ -51,7 +51,7 @@ internal class SetCryptoCurrencyActionsTransformer(
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,
onClick = {
clickIntents.onBuyClick(
userWalletId = portfolioId.userWalletId,
accountId = accountId,
cryptoCurrencyStatus = cryptoCurrencyStatus,
unavailabilityReason = action.unavailabilityReason,
)
@ -64,7 +64,7 @@ internal class SetCryptoCurrencyActionsTransformer(
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,
onClick = {
clickIntents.onReceiveClick(
portfolioId.userWalletId,
accountId,
cryptoCurrencyStatus = cryptoCurrencyStatus,
)
},
@ -91,7 +91,7 @@ internal class SetCryptoCurrencyActionsTransformer(
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,
onClick = {
clickIntents.onSendClick(
userWalletId = portfolioId.userWalletId,
accountId = accountId,
cryptoCurrencyStatus = cryptoCurrencyStatus,
unavailabilityReason = action.unavailabilityReason,
)

View file

@ -1,16 +1,17 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.domain.account.models.AccountStatusList
import com.tangem.domain.models.PortfolioId
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.tokenlist.TokenList
sealed interface TokenConverterParams {
/** Wallet mode; list of tokens for main account */
data class Wallet(
val portfolioId: PortfolioId,
val accountId: AccountId,
val tokenList: TokenList,
) : TokenConverterParams
/** Account mode; list of accounts */
data class Account(
val accountList: AccountStatusList,
val expandedAccounts: Set<AccountId>,

View file

@ -3,9 +3,9 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers.convert
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.feature.wallet.child.wallet.model.intents.WalletCurrencyActionsClickIntents
@ -18,11 +18,10 @@ import kotlinx.collections.immutable.toImmutableList
internal class MultiWalletCurrencyActionsConverter(
private val userWallet: UserWallet,
private val accountId: AccountId,
private val clickIntents: WalletCurrencyActionsClickIntents,
) : Converter<TokenActionsState, ImmutableList<TokenActionButtonConfig>> {
private val userWalletId: UserWalletId = userWallet.walletId
override fun convert(value: TokenActionsState): ImmutableList<TokenActionButtonConfig> {
return value.states
.filterIfSingleWithToken()
@ -58,17 +57,17 @@ internal class MultiWalletCurrencyActionsConverter(
is TokenActionsState.ActionState.Buy -> {
title = resourceReference(R.string.common_buy)
icon = R.drawable.ic_plus_24
action = { clickIntents.onBuyClick(userWalletId, cryptoCurrencyStatus, noneReason) }
action = { clickIntents.onBuyClick(accountId, cryptoCurrencyStatus, noneReason) }
}
is TokenActionsState.ActionState.Receive -> {
title = resourceReference(R.string.common_receive)
icon = R.drawable.ic_arrow_down_24
action = { clickIntents.onReceiveClick(userWalletId, cryptoCurrencyStatus) }
action = { clickIntents.onReceiveClick(accountId, cryptoCurrencyStatus) }
}
is TokenActionsState.ActionState.Stake -> {
title = resourceReference(R.string.common_stake)
icon = R.drawable.ic_staking_24
action = { clickIntents.onStakeClick(userWalletId, cryptoCurrencyStatus, actionsState.option) }
action = { clickIntents.onStakeClick(accountId, cryptoCurrencyStatus, actionsState.option) }
}
is TokenActionsState.ActionState.Sell -> {
title = resourceReference(R.string.common_sell)
@ -78,7 +77,7 @@ internal class MultiWalletCurrencyActionsConverter(
is TokenActionsState.ActionState.Send -> {
title = resourceReference(R.string.common_send)
icon = R.drawable.ic_arrow_up_24
action = { clickIntents.onSendClick(userWalletId, cryptoCurrencyStatus, noneReason) }
action = { clickIntents.onSendClick(accountId, cryptoCurrencyStatus, noneReason) }
}
is TokenActionsState.ActionState.Swap -> {
title = resourceReference(R.string.swapping_swap_action)
@ -86,7 +85,7 @@ internal class MultiWalletCurrencyActionsConverter(
action = {
clickIntents.onSwapClick(
cryptoCurrencyStatus = cryptoCurrencyStatus,
userWalletId = userWalletId,
accountId = accountId,
unavailabilityReason = noneReason,
)
}
@ -94,12 +93,12 @@ internal class MultiWalletCurrencyActionsConverter(
is TokenActionsState.ActionState.CopyAddress -> {
title = resourceReference(R.string.common_copy_address)
icon = R.drawable.ic_copy_24
action = { clickIntents.onCopyAddressClick(userWalletId, cryptoCurrencyStatus) }
action = { clickIntents.onCopyAddressClick(accountId, cryptoCurrencyStatus) }
}
is TokenActionsState.ActionState.HideToken -> {
title = resourceReference(R.string.token_details_hide_token)
icon = R.drawable.ic_hide_24
action = { clickIntents.onHideTokensClick(userWalletId, cryptoCurrencyStatus) }
action = { clickIntents.onHideTokensClick(accountId, cryptoCurrencyStatus) }
}
is TokenActionsState.ActionState.Analytics -> {
title = resourceReference(R.string.common_analytics)

View file

@ -1,7 +1,9 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
import com.tangem.common.ui.account.AccountCryptoPortfolioItemStateConverter
import com.tangem.common.ui.account.TokensListPortfolioItemConverter
import com.tangem.common.ui.tokens.TokenItemStateConverter
import com.tangem.core.ui.components.tokenlist.state.PortfolioItemContentUM
import com.tangem.core.ui.components.tokenlist.state.PortfolioTokensListItemUM
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
import com.tangem.core.ui.extensions.resourceReference
@ -9,7 +11,6 @@ import com.tangem.core.ui.extensions.wrappedList
import com.tangem.domain.account.models.AccountStatusList
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.PortfolioId
import com.tangem.domain.models.TotalFiatBalance
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountId
@ -49,35 +50,39 @@ internal class TokenListStateConverter(
shouldShowMainPromo,
)
private val onTokenClick: (accountId: AccountId?, currencyStatus: CryptoCurrencyStatus) -> Unit =
private val onTokenClick: (accountId: AccountId, currencyStatus: CryptoCurrencyStatus) -> Unit =
{ accountId, currencyStatus ->
clickIntents.onTokenItemClick(selectedWallet.walletId, currencyStatus)
clickIntents.onTokenItemClick(accountId, currencyStatus)
}
private val onTokenLongClick: (accountId: AccountId?, currencyStatus: CryptoCurrencyStatus) -> Unit =
private val onTokenLongClick: (accountId: AccountId, currencyStatus: CryptoCurrencyStatus) -> Unit =
{ accountId, currencyStatus ->
clickIntents.onTokenItemLongClick(selectedWallet.walletId, currencyStatus)
clickIntents.onTokenItemLongClick(accountId, currencyStatus)
}
private val onApyLabelClick:
(currencyStatus: CryptoCurrencyStatus, apySource: TokenItemStateConverter.ApySource, apy: String) -> Unit =
{ currencyStatus, apySource, apy ->
private val onApyLabelClick: (
currencyStatus: CryptoCurrencyStatus,
accountId: AccountId,
apySource: TokenItemStateConverter.ApySource,
apy: String,
) -> Unit =
{ currencyStatus, accountId, apySource, apy ->
clickIntents.onApyLabelClick(
userWalletId = selectedWallet.walletId,
accountId = accountId,
currencyStatus = currencyStatus,
apySource = apySource,
apy = apy,
)
}
private fun tokenStatusConverter(accountId: AccountId? = null) = TokenItemStateConverter(
private fun tokenStatusConverter(accountId: AccountId) = TokenItemStateConverter(
appCurrency = appCurrency,
yieldModuleApyMap = yieldModuleApyMap,
promoCryptoCurrencyStatus = yieldSupplyPromoBannerConverter.convert(params),
stakingApyMap = stakingAvailabilityMap,
onItemClick = { _, status -> onTokenClick(accountId, status) },
onItemLongClick = { _, status -> onTokenLongClick(accountId, status) },
onApyLabelClick = { status, apySource, apy -> onApyLabelClick(status, apySource, apy) },
onApyLabelClick = { status, apySource, apy -> onApyLabelClick(status, accountId, apySource, apy) },
onYieldPromoCloseClick = clickIntents::onYieldPromoCloseClick,
onYieldPromoShown = clickIntents::onYieldPromoShown,
onYieldPromoClicked = clickIntents::onYieldPromoClicked,
@ -87,7 +92,7 @@ internal class TokenListStateConverter(
return when (params) {
is TokenConverterParams.Account -> convertAccountList(params)
is TokenConverterParams.Wallet -> convertTokenList(
tokenConverter = tokenStatusConverter((params.portfolioId as? PortfolioId.Account)?.accountId),
tokenConverter = tokenStatusConverter(params.accountId),
tokenList = params.tokenList,
)
}
@ -136,12 +141,17 @@ internal class TokenListStateConverter(
is WalletTokensListState.ContentState.Locked -> tokensListState.items
is WalletTokensListState.Empty -> listOf()
}
return TokensListItemUM.Portfolio(
val onEmptyAction = PortfolioItemContentUM.Empty.Action(
text = resourceReference(id = R.string.main_manage_tokens),
onClick = { clickIntents.onManageTokensClick(account.accountId) },
)
return TokensListPortfolioItemConverter(
tokenItemUM = accountItem,
isExpanded = isExtend,
isCollapsable = true,
tokens = items.filterIsInstance<PortfolioTokensListItemUM>().toPersistentList(),
)
onEmptyAction = onEmptyAction,
).convert(Unit)
}
val accountItems = accountList.accountStatuses

View file

@ -17,9 +17,9 @@ import com.tangem.core.ui.format.bigdecimal.*
import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.feature.wallet.child.wallet.model.intents.WalletContentClickIntents
import com.tangem.feature.wallet.impl.R
@ -33,11 +33,12 @@ import java.math.BigDecimal
internal class WalletTokenCurrencyItemConverter(
private val appCurrency: AppCurrency,
private val selectedWallet: UserWallet,
private val accountId: AccountId,
private val shouldShowPromo: Boolean,
private val yieldModuleApyMap: Map<String, BigDecimal>,
private val clickIntents: WalletContentClickIntents,
stakingAvailabilityMap: Map<CryptoCurrency, StakingAvailability>,
) : Converter<Pair<CryptoCurrencyStatus, Boolean>, TangemTokenRowUM> {
) : Converter<CryptoCurrencyStatus, TangemTokenRowUM> {
private val currencyToIconStateConverter = CryptoCurrencyToIconStateConverter()
private val earnApyConverter = EarnApyConverter(
@ -45,8 +46,7 @@ internal class WalletTokenCurrencyItemConverter(
stakingApyMap = stakingAvailabilityMap,
)
override fun convert(value: Pair<CryptoCurrencyStatus, Boolean>): TangemTokenRowUM {
val (currencyStatus, shouldShowPromo) = value
override fun convert(currencyStatus: CryptoCurrencyStatus): TangemTokenRowUM {
val earnApyInfo = earnApyConverter.convert(currencyStatus)
return TangemTokenRowUM.Content(
@ -59,6 +59,7 @@ internal class WalletTokenCurrencyItemConverter(
topEndContentUM = toCurrencyRowTopEnd(currencyStatus),
bottomEndContentUM = toCurrencyRowBottomEnd(currencyStatus),
promoBannerUM = toPromoBannerUM(
accountId,
currencyStatus,
earnApyInfo.takeIf { shouldShowPromo },
),
@ -68,7 +69,7 @@ internal class WalletTokenCurrencyItemConverter(
-> null
else -> {
{
clickIntents.onTokenItemClick(selectedWallet.walletId, currencyStatus)
clickIntents.onTokenItemClick(accountId, currencyStatus)
}
}
},
@ -76,7 +77,7 @@ internal class WalletTokenCurrencyItemConverter(
CryptoCurrencyStatus.Loading -> null
else -> {
{
clickIntents.onTokenItemLongClick(selectedWallet.walletId, currencyStatus)
clickIntents.onTokenItemLongClick(accountId, currencyStatus)
}
}
},
@ -117,7 +118,7 @@ internal class WalletTokenCurrencyItemConverter(
onClick = if (earnApyInfo.apy != null) {
{
clickIntents.onApyLabelClick(
userWalletId = selectedWallet.walletId,
accountId = accountId,
currencyStatus = currencyStatus,
apySource = earnApyInfo.source,
apy = earnApyInfo.apy,
@ -261,6 +262,7 @@ internal class WalletTokenCurrencyItemConverter(
}
private fun toPromoBannerUM(
accountId: AccountId,
currencyStatus: CryptoCurrencyStatus,
earnApyInfo: EarnApyConverter.EarnApyInfo?,
): TangemTokenRowUM.PromoBannerUM {
@ -281,7 +283,7 @@ internal class WalletTokenCurrencyItemConverter(
onPromoBannerClick = {
clickIntents.onYieldPromoClicked(currency)
clickIntents.onApyLabelClick(
userWalletId = selectedWallet.walletId,
accountId = accountId,
currencyStatus = currencyStatus,
apySource = earnApyInfo.source,
apy = earnApyInfo.apy,

View file

@ -35,7 +35,7 @@ internal class WalletTokensListUMConverter(
private val yieldModuleApyMap: Map<String, BigDecimal>,
private val isAccountsModeEnabled: Boolean,
private val expandedAccounts: Set<AccountId>,
stakingAvailabilityMap: Map<CryptoCurrency, StakingAvailability>,
private val stakingAvailabilityMap: Map<CryptoCurrency, StakingAvailability>,
shouldShowMainPromo: Boolean,
) : Converter<AccountStatusList, WalletTokensListUM> {
@ -48,15 +48,6 @@ internal class WalletTokensListUMConverter(
)
}
private val currencyRowConverter by lazy(LazyThreadSafetyMode.NONE) {
WalletTokenCurrencyItemConverter(
appCurrency = appCurrency,
selectedWallet = selectedWallet,
yieldModuleApyMap = yieldModuleApyMap,
stakingAvailabilityMap = stakingAvailabilityMap,
clickIntents = clickIntents,
)
}
private val yieldSupplyPromoBannerConverter by lazy(LazyThreadSafetyMode.NONE) {
YieldSupplyPromoBannerConverter(
yieldModuleApyMap,
@ -64,6 +55,20 @@ internal class WalletTokensListUMConverter(
)
}
private fun currencyRowConverter(
accountId: AccountId,
shouldShowPromo: Boolean,
): WalletTokenCurrencyItemConverter {
return WalletTokenCurrencyItemConverter(
appCurrency = appCurrency,
accountId = accountId,
shouldShowPromo = shouldShowPromo,
yieldModuleApyMap = yieldModuleApyMap,
stakingAvailabilityMap = stakingAvailabilityMap,
clickIntents = clickIntents,
)
}
override fun convert(value: AccountStatusList): WalletTokensListUM {
val promoCryptoCurrency = yieldSupplyPromoBannerConverter.convert2(value = value)
return if (value.accountStatuses.isEmpty()) {
@ -85,13 +90,13 @@ internal class WalletTokensListUMConverter(
isExpanded = isExpanded || !isCollapsable,
isCollapsable = isCollapsable,
tokenList = getTokenListItems(
accountStatus.tokenList,
accountStatus,
promoCryptoCurrency,
).toPersistentList(),
),
)
} else {
getTokenListItems(accountStatus.tokenList, promoCryptoCurrency)
getTokenListItems(accountStatus, promoCryptoCurrency)
}
}.toPersistentList()
@ -103,10 +108,10 @@ internal class WalletTokensListUMConverter(
}
private fun getTokenListItems(
tokenList: TokenList,
accountStatus: AccountStatus.CryptoPortfolio,
promoCryptoCurrency: CryptoCurrencyStatus?,
): Sequence<TokensListItemUM2> {
return when (tokenList) {
return when (val tokenList = accountStatus.tokenList) {
TokenList.Empty -> emptySequence()
is TokenList.GroupedByNetwork -> {
tokenList.groups.asSequence().flatMap { (network, currencies) ->
@ -120,7 +125,10 @@ internal class WalletTokensListUMConverter(
currencies.asSequence().map { currencyStatus ->
val shouldShowPromo = promoCryptoCurrency?.currency?.id == currencyStatus.currency.id
TokensListItemUM2.Token(
tokenRowUM = currencyRowConverter.convert(currencyStatus to shouldShowPromo),
tokenRowUM = currencyRowConverter(
accountStatus.accountId,
shouldShowPromo,
).convert(currencyStatus),
)
}.toList(),
)
@ -131,7 +139,10 @@ internal class WalletTokensListUMConverter(
tokenList.currencies.asSequence().map { currencyStatus ->
val shouldShowPromo = promoCryptoCurrency?.currency?.id == currencyStatus.currency.id
TokensListItemUM2.Token(
tokenRowUM = currencyRowConverter.convert(currencyStatus to shouldShowPromo),
tokenRowUM = currencyRowConverter(
accountStatus.accountId,
shouldShowPromo,
).convert(currencyStatus),
)
}
}

View file

@ -6,7 +6,6 @@ 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.currency.CryptoCurrency
import com.tangem.domain.models.tokenlist.TokenList
@ -66,7 +65,7 @@ internal abstract class BasicAccountListSubscriber : BasicWalletSubscriber() {
singleAccountTransform(
maybeTokenList = maybeTokenList,
appCurrency = appCurrency,
portfolioId = PortfolioId(mainAccount.accountId),
accountId = mainAccount.accountId,
yieldSupplyApyMap = yieldSupplyApyMap,
stakingAvailabilityMap = stakingAvailabilityMap,
shouldShowMainPromo = shouldShowMainPromo,
@ -111,7 +110,7 @@ internal abstract class BasicAccountListSubscriber : BasicWalletSubscriber() {
private fun singleAccountTransform(
maybeTokenList: Lce<TokenListError, TokenList>,
appCurrency: AppCurrency,
portfolioId: PortfolioId,
accountId: AccountId,
yieldSupplyApyMap: Map<String, BigDecimal> = emptyMap(),
stakingAvailabilityMap: Map<CryptoCurrency, StakingAvailability> = emptyMap(),
shouldShowMainPromo: Boolean,
@ -140,7 +139,7 @@ internal abstract class BasicAccountListSubscriber : BasicWalletSubscriber() {
)
updateContent(
params = TokenConverterParams.Wallet(portfolioId, tokenList),
params = TokenConverterParams.Wallet(accountId, tokenList),
appCurrency = appCurrency,
yieldSupplyApyMap = yieldSupplyApyMap,
stakingAvailabilityMap = stakingAvailabilityMap,

View file

@ -2,7 +2,6 @@ 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
@ -33,17 +32,17 @@ internal class SingleWalletButtonsSubscriber @AssistedInject constructor(
getCryptoCurrencyActionsUseCaseV2(accountId = accountId, currency = it.currency)
}
.onEach {
updateContent(tokenActionsState = it, portfolioId = PortfolioId(userWallet.walletId))
updateContent(tokenActionsState = it)
}
}
private fun updateContent(tokenActionsState: TokenActionsState, portfolioId: PortfolioId) {
private fun updateContent(tokenActionsState: TokenActionsState) {
stateController.update(
SetCryptoCurrencyActionsTransformer(
tokenActionsState = tokenActionsState,
userWallet = userWallet,
clickIntents = clickIntents,
portfolioId = portfolioId,
accountId = accountId,
),
)
}

View file

@ -3,22 +3,23 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrenc
import androidx.compose.animation.*
import androidx.compose.animation.core.FastOutLinearInEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.SpacerH16
import com.tangem.core.ui.components.SpacerH24
import com.tangem.core.ui.components.buttons.SecondarySmallButton
import com.tangem.core.ui.components.buttons.SmallButtonConfig
import com.tangem.core.ui.components.tokenlist.PortfolioListItem
import com.tangem.core.ui.components.tokenlist.PortfolioTokensListItem
import com.tangem.core.ui.components.tokenlist.state.PortfolioItemContentUM
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
import com.tangem.core.ui.res.TangemTheme
@ -58,7 +59,8 @@ internal fun LazyListScope.portfolioTokensList(
portfolioIndex = portfolioIndex,
isBalanceHidden = isBalanceHidden,
)
if (tokens.isEmpty()) {
val portfolioContent = portfolio.content
if (portfolioContent is PortfolioItemContentUM.Empty) {
item(
key = "$NON_CONTENT_TOKENS_LIST_KEY account-${portfolio.id}",
contentType = "$NON_CONTENT_TOKENS_LIST_KEY account-${portfolio.id}",
@ -76,9 +78,7 @@ internal fun LazyListScope.portfolioTokensList(
),
visible = isExpanded,
) {
NonContentItemContent(
modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing28),
)
EmptyAccountContent(portfolioContent)
}
}
return
@ -114,6 +114,28 @@ internal fun LazyListScope.portfolioTokensList(
)
}
@Composable
private fun EmptyAccountContent(emptyConent: PortfolioItemContentUM.Empty, modifier: Modifier = Modifier) {
Column(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally,
) {
SpacerH16()
NonContentItemContent()
val emptyAction = emptyConent.action
if (emptyAction != null) {
SpacerH16()
SecondarySmallButton(
config = SmallButtonConfig(
text = emptyAction.text,
onClick = { emptyAction.onClick() },
),
)
}
SpacerH24()
}
}
@Suppress("MagicNumber")
private fun LazyListScope.portfolioItem(
portfolio: TokensListItemUM.Portfolio,

View file

@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers.convert
import com.google.common.truth.Truth.assertThat
import com.tangem.domain.models.PortfolioId
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.network.Network
@ -22,7 +23,7 @@ class YieldSupplyPromoBannerConverterTest {
val status = createLoadedStatus(token = token, amount = BigDecimal.ONE, isYieldActive = false)
val tokenList = ungroupedTokenList(status)
val params = TokenConverterParams.Wallet(
portfolioId = PortfolioId.Wallet(UserWalletId("00")),
accountId = AccountId.forMainCryptoPortfolio(UserWalletId("00")),
tokenList = tokenList,
)
val converter = YieldSupplyPromoBannerConverter(
@ -40,7 +41,7 @@ class YieldSupplyPromoBannerConverterTest {
val token = createToken(networkId = "ethereum", backendId = "ethereum", contract = "0xA1")
val status = createLoadedStatus(token = token, amount = BigDecimal("2.0"), isYieldActive = false)
val params = TokenConverterParams.Wallet(
portfolioId = PortfolioId.Wallet(UserWalletId("00")),
accountId = AccountId.forMainCryptoPortfolio(UserWalletId("00")),
tokenList = ungroupedTokenList(status),
)
val converter = YieldSupplyPromoBannerConverter(
@ -58,7 +59,7 @@ class YieldSupplyPromoBannerConverterTest {
val token = createToken(networkId = "ethereum", backendId = "ethereum", contract = "0xAA")
val statusActive = createLoadedStatus(token = token, amount = BigDecimal("5"), isYieldActive = true)
val params = TokenConverterParams.Wallet(
portfolioId = PortfolioId.Wallet(UserWalletId("00")),
accountId = AccountId.forMainCryptoPortfolio(UserWalletId("00")),
tokenList = ungroupedTokenList(statusActive),
)
val converter = YieldSupplyPromoBannerConverter(
@ -86,7 +87,7 @@ class YieldSupplyPromoBannerConverterTest {
)
val params = TokenConverterParams.Wallet(
portfolioId = PortfolioId.Wallet(UserWalletId("00")),
accountId = AccountId.forMainCryptoPortfolio(UserWalletId("00")),
tokenList = ungroupedTokenList(statusSmall, statusBig),
)
val converter = YieldSupplyPromoBannerConverter(
@ -109,7 +110,7 @@ class YieldSupplyPromoBannerConverterTest {
val apyMap = mapOf(mismatchedKey to BigDecimal("0.07"))
val params = TokenConverterParams.Wallet(
portfolioId = PortfolioId.Wallet(UserWalletId("00")),
accountId = AccountId.forMainCryptoPortfolio(UserWalletId("00")),
tokenList = ungroupedTokenList(status),
)
val converter = YieldSupplyPromoBannerConverter(
@ -127,7 +128,7 @@ class YieldSupplyPromoBannerConverterTest {
val token = createToken(networkId = "ethereum", backendId = "ethereum", contract = "0xCUSTOM")
val status = createCustomStatus(token = token, amount = BigDecimal("5.0"), isYieldActive = false)
val params = TokenConverterParams.Wallet(
portfolioId = PortfolioId.Wallet(UserWalletId("00")),
accountId = AccountId.forMainCryptoPortfolio(UserWalletId("00")),
tokenList = ungroupedTokenList(status),
)
val converter = YieldSupplyPromoBannerConverter(