Updated on 2026-08-14

This commit is contained in:
Tangem 2026-07-07 13:24:17 +05:00
parent 14726d2fb4
commit 5b031d5c0e
15 changed files with 448 additions and 70 deletions

View file

@ -20,6 +20,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.tangem.common.ui.R import com.tangem.common.ui.R
import com.tangem.common.ui.userwallet.CardImage
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.ui.components.SpacerW12 import com.tangem.core.ui.components.SpacerW12
import com.tangem.core.ui.components.account.AccountIconSize import com.tangem.core.ui.components.account.AccountIconSize
import com.tangem.core.ui.ds.row.TangemRowContainer import com.tangem.core.ui.ds.row.TangemRowContainer
@ -111,6 +113,18 @@ fun PortfolioSelectRowV2(
size = AccountIconSize.RedesignedDefault, size = AccountIconSize.RedesignedDefault,
) )
} }
} else if (state.imageState != null) {
Box(
modifier = Modifier
.layoutId(TangemRowLayoutId.HEAD)
.padding(end = TangemTheme.dimens2.x3),
contentAlignment = Alignment.Center,
) {
CardImage(
modifier = Modifier.size(TangemTheme.dimens2.x10),
imageState = state.imageState,
)
}
} }
Row( Row(
@ -156,6 +170,7 @@ data class PortfolioSelectUM(
val isAccountMode: Boolean, val isAccountMode: Boolean,
val isMultiChoice: Boolean, val isMultiChoice: Boolean,
val onClick: () -> Unit, val onClick: () -> Unit,
val imageState: UserWalletItemUM.ImageState? = null,
) )
@Preview(widthDp = 360, showBackground = true) @Preview(widthDp = 360, showBackground = true)

View file

@ -727,6 +727,7 @@
<string name="generic_error">An error occurred</string> <string name="generic_error">An error occurred</string>
<string name="generic_error_code">An error occurred. Code: %s.</string> <string name="generic_error_code">An error occurred. Code: %s.</string>
<string name="generic_requires_memo_error">Requires memo</string> <string name="generic_requires_memo_error">Requires memo</string>
<string name="get_token_title">Get %1$s</string>
<string name="give_permission_current_transaction">Transaction</string> <string name="give_permission_current_transaction">Transaction</string>
<string name="give_permission_policy_type_footer">By approving, you allow the smart contract to use your tokens in future transactions.</string> <string name="give_permission_policy_type_footer">By approving, you allow the smart contract to use your tokens in future transactions.</string>
<string name="give_permission_rows_amount">Amount %s</string> <string name="give_permission_rows_amount">Amount %s</string>

View file

@ -18,6 +18,7 @@ import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTi
import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioSelectorComponent import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioSelectorComponent
import com.tangem.features.commonfeatures.impl.R import com.tangem.features.commonfeatures.impl.R
@ -69,7 +70,7 @@ internal fun AddToPortfolioBottomSheet(
AddToPortfolioRoutes.AddToken, AddToPortfolioRoutes.AddToken,
AddToPortfolioRoutes.Empty, AddToPortfolioRoutes.Empty,
is AddToPortfolioRoutes.NetworkSelector, is AddToPortfolioRoutes.NetworkSelector,
AddToPortfolioRoutes.TokenActions, is AddToPortfolioRoutes.TokenActions,
-> true -> true
} }
if (isScrollableContent) { if (isScrollableContent) {
@ -92,11 +93,12 @@ private fun AddToPortfolioBottomSheetTitle(
onBackClick: () -> Unit, onBackClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val title: TextReference = when (stack.active.configuration) { val title: TextReference = when (val config = stack.active.configuration) {
AddToPortfolioRoutes.AddToken -> resourceReference(R.string.common_add_token) AddToPortfolioRoutes.AddToken -> resourceReference(R.string.common_add_token)
AddToPortfolioRoutes.Empty -> TextReference.EMPTY AddToPortfolioRoutes.Empty -> TextReference.EMPTY
is AddToPortfolioRoutes.NetworkSelector -> resourceReference(R.string.common_choose_network) is AddToPortfolioRoutes.NetworkSelector -> resourceReference(R.string.common_choose_network)
AddToPortfolioRoutes.TokenActions -> resourceReference(R.string.common_get_token) is AddToPortfolioRoutes.TokenActions ->
resourceReference(R.string.get_token_title, wrappedList(config.currencyName))
AddToPortfolioRoutes.UserPortfolio -> resourceReference(R.string.markets_portfolio_block_title) AddToPortfolioRoutes.UserPortfolio -> resourceReference(R.string.markets_portfolio_block_title)
AddToPortfolioRoutes.PortfolioSelector -> (stack.active.instance as PortfolioSelectorComponent) AddToPortfolioRoutes.PortfolioSelector -> (stack.active.instance as PortfolioSelectorComponent)
.title.collectAsStateWithLifecycle().value .title.collectAsStateWithLifecycle().value

View file

@ -104,7 +104,7 @@ internal class DefaultAddToPortfolioComponent @AssistedInject constructor(
): ComposableContentComponent = when (config) { ): ComposableContentComponent = when (config) {
AddToPortfolioRoutes.AddToken -> addTokenComponent AddToPortfolioRoutes.AddToken -> addTokenComponent
AddToPortfolioRoutes.PortfolioSelector -> portfolioSelectorComponent AddToPortfolioRoutes.PortfolioSelector -> portfolioSelectorComponent
AddToPortfolioRoutes.TokenActions -> tokenActionsComponent is AddToPortfolioRoutes.TokenActions -> tokenActionsComponent
AddToPortfolioRoutes.Empty -> ComposableContentComponent.EMPTY AddToPortfolioRoutes.Empty -> ComposableContentComponent.EMPTY
AddToPortfolioRoutes.UserPortfolio -> createUserPortfolioComponent(componentContext) AddToPortfolioRoutes.UserPortfolio -> createUserPortfolioComponent(componentContext)
is AddToPortfolioRoutes.NetworkSelector -> chooseNetworkComponentFactory.create( is AddToPortfolioRoutes.NetworkSelector -> chooseNetworkComponentFactory.create(

View file

@ -305,7 +305,9 @@ internal class AddToPortfolioModel @Inject constructor(
setupTokenActionsFlow(selectedPortfolioSnapshot, addedToken) setupTokenActionsFlow(selectedPortfolioSnapshot, addedToken)
.onEach { cryptoCurrencyData -> .onEach { cryptoCurrencyData ->
tokenActionsData.emit(cryptoCurrencyData) tokenActionsData.emit(cryptoCurrencyData)
navigation.replaceAll(AddToPortfolioRoutes.TokenActions) navigation.replaceAll(
AddToPortfolioRoutes.TokenActions(cryptoCurrencyData.status.currency.name),
)
} }
.onEmpty { finishSuccessFlow(result) } .onEmpty { finishSuccessFlow(result) }
.launchIn(this) .launchIn(this)

View file

@ -2,6 +2,7 @@ package com.tangem.features.commonfeatures.impl.addtoportfolio.model
import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.features.commonfeatures.impl.R import com.tangem.features.commonfeatures.impl.R
internal data class AddToPortfolioRouteUiSpec( internal data class AddToPortfolioRouteUiSpec(
@ -42,8 +43,8 @@ internal fun AddToPortfolioRoutes.uiSpec(): AddToPortfolioRouteUiSpec = when (th
shouldApplyHorizontalPadding = false, shouldApplyHorizontalPadding = false,
footer = AddToPortfolioFooterKind.UserPortfolioAdd, footer = AddToPortfolioFooterKind.UserPortfolioAdd,
) )
AddToPortfolioRoutes.TokenActions -> AddToPortfolioRouteUiSpec( is AddToPortfolioRoutes.TokenActions -> AddToPortfolioRouteUiSpec(
title = resourceReference(R.string.common_get_token), title = resourceReference(R.string.get_token_title, wrappedList(currencyName)),
isScrollable = false, isScrollable = false,
shouldApplyHorizontalPadding = true, shouldApplyHorizontalPadding = true,
footer = AddToPortfolioFooterKind.None, footer = AddToPortfolioFooterKind.None,

View file

@ -27,5 +27,5 @@ internal sealed interface AddToPortfolioRoutes : Route {
data object UserPortfolio : AddToPortfolioRoutes data object UserPortfolio : AddToPortfolioRoutes
@Serializable @Serializable
data object TokenActions : AddToPortfolioRoutes data class TokenActions(val currencyName: String) : AddToPortfolioRoutes
} }

View file

@ -17,18 +17,23 @@ import com.tangem.features.commonfeatures.api.addtoportfolio.SelectedPortfolio
import com.tangem.features.commonfeatures.impl.R import com.tangem.features.commonfeatures.impl.R
import com.tangem.features.commonfeatures.impl.addtoportfolio.AddTokenComponent import com.tangem.features.commonfeatures.impl.addtoportfolio.AddTokenComponent
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddTokenUiBuilder.Companion.toggleProgress import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddTokenUiBuilder.Companion.toggleProgress
import com.tangem.features.wallet.utils.UserWalletImageFetcher
import com.tangem.lib.crypto.BlockchainUtils import com.tangem.lib.crypto.BlockchainUtils
import com.tangem.operations.attestation.ArtworkSize
import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.JobHolder import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.saveIn import com.tangem.utils.coroutines.saveIn
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@ModelScoped @ModelScoped
@Suppress("LongParameterList") @Suppress("LongParameterList")
@OptIn(ExperimentalCoroutinesApi::class)
internal class AddTokenModel @Inject constructor( internal class AddTokenModel @Inject constructor(
paramsContainer: ParamsContainer, paramsContainer: ParamsContainer,
private val walletImageFetcher: UserWalletImageFetcher,
private val uiBuilder: AddTokenUiBuilder, private val uiBuilder: AddTokenUiBuilder,
private val messageSender: UiMessageSender, private val messageSender: UiMessageSender,
private val coldWalletAndHasMissedDerivationsUseCase: ColdWalletAndHasMissedDerivationsUseCase, private val coldWalletAndHasMissedDerivationsUseCase: ColdWalletAndHasMissedDerivationsUseCase,
@ -46,15 +51,22 @@ internal class AddTokenModel @Inject constructor(
field = MutableStateFlow(value = null) field = MutableStateFlow(value = null)
init { init {
val walletImageFlow = params.selectedPortfolio
.map { it.userWallet }
.distinctUntilChanged()
.flatMapLatest { walletImageFetcher.walletImage(it, ArtworkSize.SMALL) }
combine( combine(
flow = params.selectedNetwork.distinctUntilChanged(), flow = params.selectedNetwork.distinctUntilChanged(),
flow2 = params.selectedPortfolio.distinctUntilChanged(), flow2 = params.selectedPortfolio.distinctUntilChanged(),
transform = { selectedNetwork, selectedPortfolio -> flow3 = walletImageFlow,
transform = { selectedNetwork, selectedPortfolio, walletImage ->
addTokenJob.join() addTokenJob.join()
val isTangemIconVisible = needColdWalletInteraction(selectedNetwork, selectedPortfolio) val isTangemIconVisible = needColdWalletInteraction(selectedNetwork, selectedPortfolio)
uiBuilder.updateContent( uiBuilder.updateContent(
selectedPortfolio = selectedPortfolio, selectedPortfolio = selectedPortfolio,
selectedNetwork = selectedNetwork, selectedNetwork = selectedNetwork,
walletImage = walletImage,
isTangemIconVisible = isTangemIconVisible, isTangemIconVisible = isTangemIconVisible,
onConfirmClick = { onAddClick(selectedNetwork, selectedPortfolio).saveIn(addTokenJob) }, onConfirmClick = { onAddClick(selectedNetwork, selectedPortfolio).saveIn(addTokenJob) },
) )

View file

@ -5,6 +5,7 @@ import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.PortfolioSelectUM import com.tangem.common.ui.account.PortfolioSelectUM
import com.tangem.common.ui.account.toUM import com.tangem.common.ui.account.toUM
import com.tangem.common.ui.addtoken.AddTokenUM import com.tangem.common.ui.addtoken.AddTokenUM
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
@ -35,13 +36,18 @@ internal class AddTokenUiBuilder @Inject constructor(
) )
} }
private fun createPortfolio(selectedPortfolio: SelectedPortfolio): PortfolioSelectUM { private fun createPortfolio(
selectedPortfolio: SelectedPortfolio,
walletImage: UserWalletItemUM.ImageState,
): PortfolioSelectUM {
val accountIcon: AccountIconUM? val accountIcon: AccountIconUM?
val portfolioName: TextReference val portfolioName: TextReference
val imageState: UserWalletItemUM.ImageState?
when (selectedPortfolio.isAccountMode) { when (selectedPortfolio.isAccountMode) {
false -> { false -> {
accountIcon = null accountIcon = null
portfolioName = stringReference(selectedPortfolio.userWallet.name) portfolioName = stringReference(selectedPortfolio.userWallet.name)
imageState = walletImage
} }
true -> { true -> {
val accountStatus = selectedPortfolio.account.account val accountStatus = selectedPortfolio.account.account
@ -50,6 +56,7 @@ internal class AddTokenUiBuilder @Inject constructor(
is CryptoPortfolio -> CryptoPortfolioIconConverter.convert(accountStatus.account.icon) is CryptoPortfolio -> CryptoPortfolioIconConverter.convert(accountStatus.account.icon)
is Payment -> AccountIconUM.Payment is Payment -> AccountIconUM.Payment
} }
imageState = null
} }
} }
return PortfolioSelectUM( return PortfolioSelectUM(
@ -58,12 +65,14 @@ internal class AddTokenUiBuilder @Inject constructor(
isAccountMode = selectedPortfolio.isAccountMode, isAccountMode = selectedPortfolio.isAccountMode,
isMultiChoice = selectedPortfolio.isAvailableMorePortfolio, isMultiChoice = selectedPortfolio.isAvailableMorePortfolio,
onClick = { params.callbacks.onChangePortfolioClick() }, onClick = { params.callbacks.onChangePortfolioClick() },
imageState = imageState,
) )
} }
fun updateContent( fun updateContent(
selectedPortfolio: SelectedPortfolio, selectedPortfolio: SelectedPortfolio,
selectedNetwork: SelectedNetwork, selectedNetwork: SelectedNetwork,
walletImage: UserWalletItemUM.ImageState,
isTangemIconVisible: Boolean, isTangemIconVisible: Boolean,
onConfirmClick: () -> Unit, onConfirmClick: () -> Unit,
): AddTokenUM { ): AddTokenUM {
@ -78,7 +87,7 @@ internal class AddTokenUiBuilder @Inject constructor(
onConfirmClick = onConfirmClick, onConfirmClick = onConfirmClick,
) )
val networkUM = createNetwork(selectedNetwork) val networkUM = createNetwork(selectedNetwork)
val portfolioUM = createPortfolio(selectedPortfolio) val portfolioUM = createPortfolio(selectedPortfolio, walletImage)
val currency = selectedNetwork.cryptoCurrency val currency = selectedNetwork.cryptoCurrency
val tokenToAdd = TokenItemState.Content( val tokenToAdd = TokenItemState.Content(
id = currency.id.value, id = currency.id.value,

View file

@ -8,7 +8,6 @@ import com.tangem.common.ui.tokens.TokenItemGrouping.toGroupedItems
import com.tangem.common.ui.tokens.TokenItemGrouping.toUngroupedItems import com.tangem.common.ui.tokens.TokenItemGrouping.toUngroupedItems
import com.tangem.common.ui.tokens.TokenItemStateConverter import com.tangem.common.ui.tokens.TokenItemStateConverter
import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.icons.IconTint
import com.tangem.core.ui.components.token.state.TokenItemState import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.components.token.state.TokenItemState.FiatAmountState import com.tangem.core.ui.components.token.state.TokenItemState.FiatAmountState
import com.tangem.core.ui.components.tokenlist.state.PortfolioTokensListItemUM import com.tangem.core.ui.components.tokenlist.state.PortfolioTokensListItemUM
@ -55,12 +54,11 @@ internal class ChooseTokenListItemConverter(
} }
private val fiatAmountStateProvider: ((TotalFiatBalance, isExpanded: Boolean) -> FiatAmountState?) = private val fiatAmountStateProvider: ((TotalFiatBalance, isExpanded: Boolean) -> FiatAmountState?) =
{ totalBalance, isExpanded -> { totalBalance, _ ->
when { if (isSearchingState) {
isSearchingState -> FiatAmountState.Empty FiatAmountState.Empty
!isExpanded -> FiatAmountState.Icon(R.drawable.ic_chewron_down_20, IconTint.Informative) } else {
else -> AccountCryptoPortfolioItemStateConverter AccountCryptoPortfolioItemStateConverter.createFiatAmountState(totalBalance, appCurrency)
.createFiatAmountState(totalBalance, appCurrency)
} }
} }

View file

@ -1,5 +1,16 @@
package com.tangem.features.commonfeatures.impl.choosetoken.ui package com.tangem.features.commonfeatures.impl.choosetoken.ui
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.BoundsTransform
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionScope.ResizeMode.Companion.scaleToBounds
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.animateIntAsState
import androidx.compose.animation.core.snap
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@ -7,6 +18,7 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.* import androidx.compose.foundation.lazy.*
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@ -15,16 +27,21 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.testTag import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.lerp
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.tangem.common.ui.tokens.portfolioTokensList import com.tangem.common.ui.tokens.NonContentItemContent
import com.tangem.common.ui.tokens.SlideInItemVisibility
import com.tangem.core.ui.components.SpacerH import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.account.AccountIconSize
import com.tangem.core.ui.components.account.toBoxSize
import com.tangem.core.ui.components.appbar.AppBarWithBackButton import com.tangem.core.ui.components.appbar.AppBarWithBackButton
import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.fields.SearchBar import com.tangem.core.ui.components.fields.SearchBar
@ -34,6 +51,8 @@ import com.tangem.core.ui.components.list.InfiniteListHandler
import com.tangem.core.ui.components.marketprice.PriceChangeType import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.components.token.AccountItemPreviewData import com.tangem.core.ui.components.token.AccountItemPreviewData
import com.tangem.core.ui.components.token.state.TokenItemState import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.components.tokenlist.NON_CONTENT_TOKENS_LIST_KEY
import com.tangem.core.ui.components.tokenlist.PortfolioTokensListItem
import com.tangem.core.ui.components.tokenlist.TokenListItem import com.tangem.core.ui.components.tokenlist.TokenListItem
import com.tangem.core.ui.components.tokenlist.state.PortfolioItemContentUM 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.PortfolioTokensListItemUM
@ -41,26 +60,42 @@ import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
import com.tangem.core.ui.decorations.roundedShapeItemDecoration import com.tangem.core.ui.decorations.roundedShapeItemDecoration
import com.tangem.core.ui.ds.image.DeviceIconUM import com.tangem.core.ui.ds.image.DeviceIconUM
import com.tangem.core.ui.ds.image.TangemDeviceIcon import com.tangem.core.ui.ds.image.TangemDeviceIcon
import com.tangem.core.ui.ds.image.TangemIcon
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.ds.row.header.TangemHeaderRow
import com.tangem.core.ui.ds.row.internal.TangemRowTailUM
import com.tangem.core.ui.ds.row.token.TangemTokenRow
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
import com.tangem.core.ui.ds.row.token.internal.TokenRowTitle
import com.tangem.core.ui.extensions.* import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.res.LocalRedesignEnabled import com.tangem.core.ui.res.LocalRedesignEnabled
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.BuyTokenScreenTestTags import com.tangem.core.ui.test.BuyTokenScreenTestTags
import com.tangem.core.ui.utils.ProvideSharedTransitionScope
import com.tangem.core.ui.utils.TangemSharedTransitionLayout import com.tangem.core.ui.utils.TangemSharedTransitionLayout
import com.tangem.core.ui.utils.lazyListItemPosition import com.tangem.core.ui.utils.lazyListItemPosition
import com.tangem.core.ui.utils.rememberHideKeyboardNestedScrollConnection import com.tangem.core.ui.utils.rememberHideKeyboardNestedScrollConnection
import com.tangem.core.ui.utils.sharedBoundsSafely
import com.tangem.features.commonfeatures.api.choosetoken.model.ChooseTokenPortfolioFullBlockUM import com.tangem.features.commonfeatures.api.choosetoken.model.ChooseTokenPortfolioFullBlockUM
import com.tangem.features.commonfeatures.api.choosetoken.model.TokenListUMData import com.tangem.features.commonfeatures.api.choosetoken.model.TokenListUMData
import com.tangem.features.commonfeatures.api.choosetoken.model.WalletListUM import com.tangem.features.commonfeatures.api.choosetoken.model.WalletListUM
import com.tangem.features.commonfeatures.api.choosetoken.model.WalletTabUM import com.tangem.features.commonfeatures.api.choosetoken.model.WalletTabUM
import com.tangem.features.commonfeatures.impl.R import com.tangem.features.commonfeatures.impl.R
import com.tangem.features.commonfeatures.impl.choosetoken.market.state.SwapMarketState import com.tangem.features.commonfeatures.impl.choosetoken.market.state.SwapMarketState
import com.tangem.utils.StringsSigns.DOT
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList import kotlinx.collections.immutable.toPersistentList
import kotlin.random.Random import kotlin.random.Random
private const val LOAD_MORE_BUFFER = 25 private const val LOAD_MORE_BUFFER = 25
private const val ACCOUNT_COLLAPSE_STEP_MS = 50
private const val ACCOUNT_COLLAPSE_MAX_DELAY_MS = 250
private const val ACCOUNT_COLLAPSE_BASE_DELAY_MS = 150
private const val ACCOUNT_CONTENT_ANIM_MS = 350
private const val ACCOUNT_CONTENT_ANIM_DELAY_MS = 90
private const val ACCOUNT_BOUNDS_ANIM_MS = 250
private val ChooseTokenFullUM.isNotFoundState: Boolean private val ChooseTokenFullUM.isNotFoundState: Boolean
get() { get() {
@ -298,11 +333,10 @@ private fun LazyListScope.tokensListItems(tokensListData: TokenListUMData, isBal
when (tokensListData) { when (tokensListData) {
is TokenListUMData.AccountList -> { is TokenListUMData.AccountList -> {
tokensListData.tokensList.forEachIndexed { index, item -> tokensListData.tokensList.forEachIndexed { index, item ->
portfolioTokensList( accountWithTokens(
portfolio = item, portfolio = item,
portfolioIndex = index, portfolioIndex = index,
isBalanceHidden = isBalanceHidden, isBalanceHidden = isBalanceHidden,
testTag = BuyTokenScreenTestTags.LAZY_LIST_ITEM,
) )
} }
} }
@ -338,6 +372,298 @@ private fun LazyListScope.tokensList(items: ImmutableList<TokensListItemUM>, isB
) )
} }
@Suppress("LongMethod")
private fun LazyListScope.accountWithTokens(
portfolio: TokensListItemUM.Portfolio,
portfolioIndex: Int,
isBalanceHidden: Boolean,
) {
val tokens = portfolio.tokens
val isExpanded = portfolio.isExpanded
val lastIndex = maxOf(tokens.lastIndex.inc(), 1)
item(key = "account-${portfolio.id}", contentType = "choose-token-account") {
val effectiveLastIndex by animateIntAsState(
targetValue = if (isExpanded) lastIndex else 0,
animationSpec = if (isExpanded) {
snap()
} else {
snap(
delayMillis = minOf(
ACCOUNT_COLLAPSE_STEP_MS * maxOf(tokens.lastIndex, 0),
ACCOUNT_COLLAPSE_MAX_DELAY_MS,
) + ACCOUNT_COLLAPSE_BASE_DELAY_MS,
)
},
label = "accountLastIndex",
)
AccountRow(
portfolio = portfolio,
isBalanceHidden = isBalanceHidden,
modifier = Modifier
.testTag(BuyTokenScreenTestTags.LAZY_LIST_ITEM)
.semantics { lazyListItemPosition = portfolioIndex }
.roundedShapeItemDecoration(
currentIndex = 0,
radius = TangemTheme.dimens.radius14,
lastIndex = effectiveLastIndex,
backgroundColor = TangemTheme.colors.background.primary,
),
)
}
if (portfolio.content is PortfolioItemContentUM.Empty) {
item(key = "$NON_CONTENT_TOKENS_LIST_KEY account-${portfolio.id}") {
SlideInItemVisibility(
visible = isExpanded,
currentIndex = 1,
lastIndex = lastIndex,
modifier = Modifier.roundedShapeItemDecoration(
currentIndex = 1,
radius = TangemTheme.dimens.radius14,
lastIndex = 1,
backgroundColor = TangemTheme.colors.background.primary,
),
) {
NonContentItemContent(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing16))
}
}
return
}
itemsIndexed(
items = tokens,
key = { _, token -> "${token.id}-choose-account-${portfolio.id}" },
contentType = { _, token -> token::class.java },
) { tokenIndex, token ->
val indexWithHeader = tokenIndex.inc()
val lastTokenBottomPadding = TangemTheme.dimens.spacing8
SlideInItemVisibility(
visible = isExpanded,
currentIndex = tokenIndex,
lastIndex = lastIndex,
modifier = Modifier
.testTag(BuyTokenScreenTestTags.LAZY_LIST_ITEM)
.roundedShapeItemDecoration(
currentIndex = indexWithHeader,
radius = TangemTheme.dimens.radius14,
lastIndex = lastIndex,
backgroundColor = TangemTheme.colors.background.primary,
),
) {
PortfolioTokensListItem(
state = token,
isBalanceHidden = isBalanceHidden,
modifier = Modifier.conditional(indexWithHeader == lastIndex) {
padding(bottom = lastTokenBottomPadding)
},
)
}
}
}
@Suppress("LongMethod")
@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
private fun AccountRow(
portfolio: TokensListItemUM.Portfolio,
isBalanceHidden: Boolean,
modifier: Modifier = Modifier,
) {
val tokenRowUM = accountTokenRowUM(portfolio)
val subtitle = (tokenRowUM.subtitleUM as? TangemTokenRowUM.SubtitleUM.Content)?.text
Row(
modifier = modifier,
verticalAlignment = Alignment.CenterVertically,
) {
ProvideSharedTransitionScope(Modifier.weight(1f)) {
val iconSharedContentState = rememberSharedContentState(key = "account-icon-${portfolio.id}")
val titleSharedContentState = rememberSharedContentState(key = "account-title-${portfolio.id}")
val boundsTransform = BoundsTransform { _, _ -> tween(ACCOUNT_BOUNDS_ANIM_MS) }
AnimatedContent(
targetState = portfolio.isExpanded,
transitionSpec = {
fadeIn(animationSpec = tween(ACCOUNT_CONTENT_ANIM_MS, delayMillis = ACCOUNT_CONTENT_ANIM_DELAY_MS))
.togetherWith(fadeOut(animationSpec = tween(ACCOUNT_CONTENT_ANIM_MS)))
},
label = "accountExpand",
) { isExpandedState ->
val animatedContentScope = this
val composables = remember(tokenRowUM) {
AccountRowComposables(
icon = { iconModifier ->
val iconSize = if (isExpandedState) {
AccountIconSize.RedesignExtraSmall
} else {
AccountIconSize.RedesignedDefault
}
val sizedIcon = when (val icon = tokenRowUM.headIconUM) {
is TangemIconUM.Currency -> icon.copy(
currencyIconState = when (val iconState = icon.currencyIconState) {
is CurrencyIconState.CryptoPortfolio.Icon -> iconState.copy(size = iconSize)
is CurrencyIconState.CryptoPortfolio.Letter -> iconState.copy(size = iconSize)
else -> iconState
},
)
else -> icon
}
TangemIcon(
tangemIconUM = sizedIcon,
modifier = iconModifier
.size(iconSize.toBoxSize())
.sharedBoundsSafely(
sharedContentState = iconSharedContentState,
animatedVisibilityScope = animatedContentScope,
boundsTransform = boundsTransform,
),
)
},
title = { titleModifier ->
val targetFraction = if (isExpandedState) 0f else 1f
val animationFraction = animateFloatAsState(
targetValue = targetFraction,
animationSpec = tween(durationMillis = ACCOUNT_CONTENT_ANIM_MS),
label = "accountTitle",
)
val startStyle = TangemTheme.typography2.captionSemibold12
val stopStyle = TangemTheme.typography2.bodySemibold16
val textStyle by remember(animationFraction.value) {
derivedStateOf { lerp(startStyle, stopStyle, animationFraction.value) }
}
val resizedTitle = when (val titleUM = tokenRowUM.titleUM) {
is TangemTokenRowUM.TitleUM.Content -> titleUM.copy(
text = styledStringReference(
titleUM.text.resolveReference(),
{ textStyle.toSpanStyle() },
),
)
else -> titleUM
}
TokenRowTitle(
titleUM = resizedTitle,
modifier = titleModifier.sharedBoundsSafely(
sharedContentState = titleSharedContentState,
animatedVisibilityScope = animatedContentScope,
boundsTransform = boundsTransform,
resizeMode = scaleToBounds(ContentScale.Fit, Alignment.CenterStart),
),
)
},
)
}
if (isExpandedState) {
TangemHeaderRow(
subtitle = subtitle,
isBalanceHidden = isBalanceHidden,
titleContent = composables.title,
headContent = composables.icon,
tailUM = TangemRowTailUM.Empty,
onItemClick = tokenRowUM.onItemClick,
)
} else {
TangemTokenRow(
tokenRowUM = tokenRowUM,
isBalanceHidden = isBalanceHidden,
headComponent = composables.icon,
titleComponent = composables.title,
)
}
}
}
AccountTail(
isExpanded = portfolio.isExpanded,
onClick = { tokenRowUM.onItemClick?.invoke() },
)
}
}
@Composable
private fun AccountTail(isExpanded: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
Box(
modifier = modifier
.padding(start = TangemTheme.dimens2.x2, end = TangemTheme.dimens2.x4)
.size(TangemTheme.dimens2.x9)
.clip(CircleShape)
.clickable(onClick = onClick),
contentAlignment = Alignment.Center,
) {
AnimatedContent(
targetState = isExpanded,
contentAlignment = Alignment.Center,
label = "accountTail",
) { expanded ->
if (expanded) {
Icon(
modifier = Modifier
.offset(x = TangemTheme.dimens.spacing2)
.size(TangemTheme.dimens2.x4),
painter = painterResource(id = R.drawable.ic_minimize_24),
tint = TangemTheme.colors2.graphic.neutral.tertiaryConstant,
contentDescription = null,
)
} else {
AccountExpandButton()
}
}
}
}
@Composable
private fun AccountExpandButton(modifier: Modifier = Modifier) {
Box(
modifier = modifier
.size(TangemTheme.dimens2.x9)
.clip(CircleShape)
.background(TangemTheme.colors2.button.backgroundSecondary),
contentAlignment = Alignment.Center,
) {
Icon(
modifier = Modifier.size(TangemTheme.dimens2.x5),
painter = painterResource(id = R.drawable.ic_chewron_down_20),
tint = TangemTheme.colors2.button.iconPrimary,
contentDescription = null,
)
}
}
@Stable
private class AccountRowComposables(
val title: @Composable (Modifier) -> Unit,
val icon: @Composable (Modifier) -> Unit,
)
private fun accountTokenRowUM(portfolio: TokensListItemUM.Portfolio): TangemTokenRowUM.Content {
val account = portfolio.tokenItemUM
val name = (account.titleState as? TokenItemState.TitleState.Content)?.text ?: TextReference.EMPTY
val tokensCount = (account.subtitleState as? TokenItemState.SubtitleState.TextContent)?.value
val balance = (account.fiatAmountState as? TokenItemState.FiatAmountState.Content)?.text
return TangemTokenRowUM.Content(
id = portfolio.id,
headIconUM = TangemIconUM.Currency(currencyIconState = account.iconState),
titleUM = TangemTokenRowUM.TitleUM.Content(text = name),
subtitleUM = buildAccountSubtitle(tokensCount, balance)
?.let { TangemTokenRowUM.SubtitleUM.Content(text = it) }
?: TangemTokenRowUM.SubtitleUM.Empty,
topEndContentUM = TangemTokenRowUM.EndContentUM.Empty,
bottomEndContentUM = TangemTokenRowUM.EndContentUM.Empty,
tailUM = TangemRowTailUM.Empty,
onItemClick = { account.onItemClick?.invoke(account) },
onItemLongClick = null,
)
}
private fun buildAccountSubtitle(tokensCount: TextReference?, balance: String?): TextReference? {
return when {
tokensCount != null && balance != null ->
combinedReference(tokensCount, stringReference(" $DOT "), stringReference(balance))
tokensCount != null -> tokensCount
balance != null -> stringReference(balance)
else -> null
}
}
private fun LazyListScope.emptyTokensList(modifier: Modifier = Modifier) { private fun LazyListScope.emptyTokensList(modifier: Modifier = Modifier) {
item("EmptyTokensList") { item("EmptyTokensList") {
Box( Box(

View file

@ -30,7 +30,6 @@ import com.tangem.common.ui.markets.action.TokenActionsContext
import com.tangem.features.commonfeatures.impl.tokenactions.TokenActionsComponent import com.tangem.features.commonfeatures.impl.tokenactions.TokenActionsComponent
import com.tangem.features.commonfeatures.impl.userportfolio.UserPortfolioComponent import com.tangem.features.commonfeatures.impl.userportfolio.UserPortfolioComponent
import com.tangem.features.commonfeatures.impl.R import com.tangem.features.commonfeatures.impl.R
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
@ -42,15 +41,12 @@ internal class DefaultManageFundsComponent @AssistedInject constructor(
chooseTokenComponentFactory: ChooseTokenComponent.Factory, chooseTokenComponentFactory: ChooseTokenComponent.Factory,
tokenActionsComponentFactory: TokenActionsComponent.Factory, tokenActionsComponentFactory: TokenActionsComponent.Factory,
userPortfolioComponentFactory: UserPortfolioComponent.Factory, userPortfolioComponentFactory: UserPortfolioComponent.Factory,
walletFeatureToggles: WalletFeatureToggles,
) : AppComponentContext by appComponentContext, ManageFundsComponent { ) : AppComponentContext by appComponentContext, ManageFundsComponent {
private val model: ManageFundsModel = getOrCreateModel(params) private val model: ManageFundsModel = getOrCreateModel(params)
private val isCompactTokenActions: Boolean = params.launchMode is ManageFundsComponent.LaunchMode.TokenActionsOnly private val isCompactTokenActions: Boolean = params.launchMode is ManageFundsComponent.LaunchMode.TokenActionsOnly
private val isAddFundsStage1Enabled: Boolean = walletFeatureToggles.isAddFundsStage1Enabled
private val tokenActionsComponent: TokenActionsComponent by lazy { private val tokenActionsComponent: TokenActionsComponent by lazy {
tokenActionsComponentFactory.create( tokenActionsComponentFactory.create(
context = child(key = "manageFundsTokenActions"), context = child(key = "manageFundsTokenActions"),
@ -107,7 +103,7 @@ internal class DefaultManageFundsComponent @AssistedInject constructor(
) )
} }
WithOptionalRedesignTheme(isEnabled = isAddFundsStage1Enabled) { TangemThemeRedesign {
TangemBottomSheet<TangemBottomSheetConfigContent.Empty>( TangemBottomSheet<TangemBottomSheetConfigContent.Empty>(
onBack = if (canGoBack) model::onBack else ::dismiss, onBack = if (canGoBack) model::onBack else ::dismiss,
config = TangemBottomSheetConfig( config = TangemBottomSheetConfig(
@ -179,7 +175,7 @@ internal class DefaultManageFundsComponent @AssistedInject constructor(
) { ) {
userPortfolioComponent.Content(modifier) userPortfolioComponent.Content(modifier)
} }
ManageFundsModel.UiRoute.TokenActions -> tokenActionsComponent.Content(modifier) is ManageFundsModel.UiRoute.TokenActions -> tokenActionsComponent.Content(modifier)
} }
} }
@ -191,8 +187,13 @@ internal class DefaultManageFundsComponent @AssistedInject constructor(
onCloseClick: () -> Unit, onCloseClick: () -> Unit,
) { ) {
val spec = route.uiSpec(model.flowType) val spec = route.uiSpec(model.flowType)
val title = if (route is ManageFundsModel.UiRoute.TokenActions) {
route.title
} else {
spec.title
}
TangemTopBar( TangemTopBar(
title = spec.title, title = title,
subtitle = spec.subtitle, subtitle = spec.subtitle,
type = TangemTopBarType.BottomSheet, type = TangemTopBarType.BottomSheet,
startContent = if (canGoBack) { startContent = if (canGoBack) {
@ -219,15 +220,6 @@ internal class DefaultManageFundsComponent @AssistedInject constructor(
) )
} }
@Composable
private fun WithOptionalRedesignTheme(isEnabled: Boolean, content: @Composable () -> Unit) {
if (isEnabled) {
TangemThemeRedesign(content = content)
} else {
content()
}
}
@AssistedFactory @AssistedFactory
interface Factory : ManageFundsComponent.Factory { interface Factory : ManageFundsComponent.Factory {
override fun create( override fun create(

View file

@ -10,6 +10,9 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.domain.account.status.supplier.MultiAccountStatusListSupplier import com.tangem.domain.account.status.supplier.MultiAccountStatusListSupplier
import com.tangem.domain.account.status.usecase.GetCryptoCurrencyActionsUseCaseV2 import com.tangem.domain.account.status.usecase.GetCryptoCurrencyActionsUseCaseV2
import com.tangem.domain.account.status.usecase.IsAccountsModeEnabledUseCase import com.tangem.domain.account.status.usecase.IsAccountsModeEnabledUseCase
@ -26,6 +29,7 @@ import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenAnalyticsPa
import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenBridge import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenBridge
import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenResult import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenResult
import com.tangem.features.commonfeatures.api.tokenactions.BottomAction import com.tangem.features.commonfeatures.api.tokenactions.BottomAction
import com.tangem.features.commonfeatures.impl.R
import com.tangem.features.commonfeatures.impl.managefunds.analytics.ManageFundsAnalyticsEvent import com.tangem.features.commonfeatures.impl.managefunds.analytics.ManageFundsAnalyticsEvent
import com.tangem.features.commonfeatures.impl.tokenactions.TokenActionsComponent import com.tangem.features.commonfeatures.impl.tokenactions.TokenActionsComponent
import com.tangem.features.commonfeatures.impl.userportfolio.state.UserPortfolioStateController import com.tangem.features.commonfeatures.impl.userportfolio.state.UserPortfolioStateController
@ -206,7 +210,7 @@ internal class ManageFundsModel @Inject constructor(
return@launch return@launch
} }
tokenActionsTrigger.value = TokenActionsRequest(wallet, match.first, match.second) tokenActionsTrigger.value = TokenActionsRequest(wallet, match.first, match.second)
replaceRoot(UiRoute.TokenActions) replaceRoot(tokenActionsRoute(match.second))
} }
} }
@ -218,7 +222,7 @@ internal class ManageFundsModel @Inject constructor(
1 -> { 1 -> {
val entry = entries.first() val entry = entries.first()
tokenActionsTrigger.value = TokenActionsRequest(entry.userWallet, entry.account, entry.status) tokenActionsTrigger.value = TokenActionsRequest(entry.userWallet, entry.account, entry.status)
replaceRoot(UiRoute.TokenActions) replaceRoot(tokenActionsRoute(entry.status))
} }
else -> { else -> {
filteredEntries.value = entries filteredEntries.value = entries
@ -261,7 +265,12 @@ internal class ManageFundsModel @Inject constructor(
private fun openTokenActions(request: TokenActionsRequest, bottomAction: BottomAction) { private fun openTokenActions(request: TokenActionsRequest, bottomAction: BottomAction) {
tokenActionsTrigger.value = request tokenActionsTrigger.value = request
currentBottomAction.value = bottomAction currentBottomAction.value = bottomAction
pushRoute(UiRoute.TokenActions) pushRoute(tokenActionsRoute(request.status))
}
private fun tokenActionsRoute(status: CryptoCurrencyStatus): UiRoute.TokenActions {
val title = resourceReference(R.string.get_token_title, wrappedList(status.currency.name))
return UiRoute.TokenActions(title = title)
} }
private fun replaceRoot(route: UiRoute) { private fun replaceRoot(route: UiRoute) {
@ -277,7 +286,7 @@ internal class ManageFundsModel @Inject constructor(
data object Loading : UiRoute data object Loading : UiRoute
data object ChooseToken : UiRoute data object ChooseToken : UiRoute
data object UserPortfolio : UiRoute data object UserPortfolio : UiRoute
data object TokenActions : UiRoute data class TokenActions(val title: TextReference) : UiRoute
} }
private data class TokenActionsRequest( private data class TokenActionsRequest(

View file

@ -33,7 +33,7 @@ internal fun ManageFundsModel.UiRoute.uiSpec(flowType: ManageFundsComponent.Flow
shouldApplyHorizontalPadding = false, shouldApplyHorizontalPadding = false,
shouldFillHeight = false, shouldFillHeight = false,
) )
ManageFundsModel.UiRoute.TokenActions -> ManageFundsRouteUiSpec( is ManageFundsModel.UiRoute.TokenActions -> ManageFundsRouteUiSpec(
title = resourceReference(if (isTransfer) R.string.common_transfer else R.string.common_get_token), title = resourceReference(if (isTransfer) R.string.common_transfer else R.string.common_get_token),
subtitle = null, subtitle = null,
shouldApplyHorizontalPadding = true, shouldApplyHorizontalPadding = true,

View file

@ -31,6 +31,7 @@ import com.tangem.features.commonfeatures.impl.R
import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.wallets.usecase.GetWalletIconUseCase import com.tangem.domain.wallets.usecase.GetWalletIconUseCase
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.commonfeatures.impl.tokenactions.TokenActionsComponent import com.tangem.features.commonfeatures.impl.tokenactions.TokenActionsComponent
import com.tangem.features.commonfeatures.impl.tokenactions.ui.state.PortfolioBadgeUM import com.tangem.features.commonfeatures.impl.tokenactions.ui.state.PortfolioBadgeUM
import com.tangem.features.commonfeatures.impl.tokenactions.ui.state.TokenActionsUM import com.tangem.features.commonfeatures.impl.tokenactions.ui.state.TokenActionsUM
@ -42,6 +43,7 @@ internal class TokenActionsUiBuilder @Inject constructor(
paramsContainer: ParamsContainer, paramsContainer: ParamsContainer,
private val designFeatureToggles: DesignFeatureToggles, private val designFeatureToggles: DesignFeatureToggles,
private val getWalletIconUseCase: GetWalletIconUseCase, private val getWalletIconUseCase: GetWalletIconUseCase,
private val getWalletsUseCase: GetWalletsUseCase,
private val walletIconUMConverter: WalletIconUMConverter, private val walletIconUMConverter: WalletIconUMConverter,
) { ) {
private val params = paramsContainer.require<TokenActionsComponent.Params>() private val params = paramsContainer.require<TokenActionsComponent.Params>()
@ -145,38 +147,47 @@ internal class TokenActionsUiBuilder @Inject constructor(
} }
private fun createPortfolioBadge(cryptoCurrencyData: CryptoCurrencyData): PortfolioBadgeUM { private fun createPortfolioBadge(cryptoCurrencyData: CryptoCurrencyData): PortfolioBadgeUM {
return if (cryptoCurrencyData.isAccountMode) { return when {
val icon = CryptoPortfolioIconConverter.convert(cryptoCurrencyData.account.account.icon) cryptoCurrencyData.isAccountMode -> {
val name = cryptoCurrencyData val icon = CryptoPortfolioIconConverter.convert(cryptoCurrencyData.account.account.icon)
.account val name = cryptoCurrencyData
.account .account
.accountName .account
.toUM() .accountName
.value .toUM()
PortfolioBadgeUM.Account( .value
badge = TangemBadgeUM( PortfolioBadgeUM.Account(
text = name, badge = TangemBadgeUM(
tangemIconUM = TangemIconUM.Icon( text = name,
iconRes = icon.value.getResId(), tangemIconUM = TangemIconUM.Icon(
tintReference = { icon.color.getUiColor() }, iconRes = icon.value.getResId(),
tintReference = { icon.color.getUiColor() },
),
size = TangemBadgeSize.X6,
shape = TangemBadgeShape.Rounded,
iconPosition = TangemBadgeIconPosition.Start,
shouldRespectIconTint = true,
), ),
size = TangemBadgeSize.X6, )
shape = TangemBadgeShape.Rounded, }
iconPosition = TangemBadgeIconPosition.Start, isSingleWallet() -> PortfolioBadgeUM.None
shouldRespectIconTint = true, else -> {
), val userWallet = cryptoCurrencyData.userWallet
) PortfolioBadgeUM.Wallet(
} else { name = stringReference(userWallet.name),
val userWallet = cryptoCurrencyData.userWallet deviceIcon = walletIconUMConverter.convert(
PortfolioBadgeUM.Wallet( getWalletIconUseCase(cryptoCurrencyData.userWallet),
name = stringReference(userWallet.name), ),
deviceIcon = walletIconUMConverter.convert( )
getWalletIconUseCase(cryptoCurrencyData.userWallet), }
),
)
} }
} }
private fun isSingleWallet(): Boolean {
val count = runCatching { getWalletsUseCase.invokeSync().size }.getOrNull() ?: return false
return count <= 1
}
private fun createSubtitle2State(status: CryptoCurrencyStatus): TokenItemState.Subtitle2State? { private fun createSubtitle2State(status: CryptoCurrencyStatus): TokenItemState.Subtitle2State? {
return when (status.value) { return when (status.value) {
is CryptoCurrencyStatus.Loaded, is CryptoCurrencyStatus.Loaded,