diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/AccountCryptoPortfolioItemStateConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/account/AccountCryptoPortfolioItemStateConverter.kt
index e2b2b60cd9..6baef4fa51 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/account/AccountCryptoPortfolioItemStateConverter.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/account/AccountCryptoPortfolioItemStateConverter.kt
@@ -3,6 +3,7 @@ package com.tangem.common.ui.account
import com.tangem.common.ui.R
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.Subtitle2State
import com.tangem.core.ui.extensions.pluralReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.format.bigdecimal.fiat
@@ -56,8 +57,8 @@ class AccountCryptoPortfolioItemStateConverter(
)
}
- private fun Account.CryptoPortfolio.mapToLoadingState(): TokenItemState.Loading {
- return TokenItemState.Loading(
+ private fun Account.CryptoPortfolio.mapToLoadingState(): TokenItemState.Content {
+ return TokenItemState.Content(
id = account.accountId.value,
iconState = AccountIconItemStateConverter.convert(account),
titleState = TokenItemState.TitleState.Content(
@@ -71,6 +72,10 @@ class AccountCryptoPortfolioItemStateConverter(
),
isAvailable = false,
),
+ fiatAmountState = FiatAmountState.Loading,
+ subtitle2State = Subtitle2State.Loading,
+ onItemLongClick = null,
+ onItemClick = onItemClick?.let { onItemClick -> { onItemClick(account) } },
)
}
diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/AccountTitle.kt b/common/ui/src/main/java/com/tangem/common/ui/account/AccountTitle.kt
index e2d1c5b4c1..45862deedb 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/account/AccountTitle.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/account/AccountTitle.kt
@@ -6,6 +6,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
@@ -29,6 +30,7 @@ fun AccountTitle(
accountTitleUM: AccountTitleUM,
modifier: Modifier = Modifier,
textStyle: TextStyle = TangemTheme.typography.subtitle2,
+ textColor: Color = TangemTheme.colors.text.tertiary,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
@@ -40,19 +42,20 @@ fun AccountTitle(
Text(
text = accountTitleUM.prefixText.resolveReference(),
style = textStyle,
- color = TangemTheme.colors.text.tertiary,
+ color = textColor,
)
AccountLabel(
name = accountTitleUM.name,
icon = accountTitleUM.icon,
iconSize = AccountIconSize.ExtraSmall,
nameStyle = textStyle,
+ nameColor = textColor,
)
}
is AccountTitleUM.Text -> Text(
text = accountTitleUM.title.resolveReference(),
style = textStyle,
- color = TangemTheme.colors.text.tertiary,
+ color = textColor,
modifier = Modifier.testTag(SendScreenTestTags.AMOUNT_CONTAINER_TITLE),
)
}
diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml
index 52be016eb1..baa7cb6f64 100644
--- a/core/res/src/main/res/values-ja/strings.xml
+++ b/core/res/src/main/res/values-ja/strings.xml
@@ -895,7 +895,7 @@
- 最大%d日
%s分
- 下記より利用可能
+ 下記より利用可能
以下が手に入ります。
サービスは外部プロバイダーによって提供されます。 \nTangemは責任を負いません。
この画面を閉じて、トークンの詳細画面で取引状況を確認できます。
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index ea88b88529..5351f7b076 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -617,6 +617,7 @@
The selected token is currently unavailable for actions within the crypto wallet. But worry not, you can express your interest by upvoting it.
Upvote
The wallet doesn\'t support more than one network
+ About coin
To buy, exchange, or receive this asset, add it to your portfolio
This asset is currently not supported in the wallet
This asset is not available for this wallet
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/DefaultMarketsTokenDetailsComponent.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/DefaultMarketsTokenDetailsComponent.kt
index 1210aa9da6..b1f66a3c20 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/DefaultMarketsTokenDetailsComponent.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/DefaultMarketsTokenDetailsComponent.kt
@@ -13,6 +13,7 @@ import com.tangem.core.decompose.context.child
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.res.LocalMainBottomSheetColor
import com.tangem.core.ui.res.TangemTheme
+import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.markets.details.MarketsTokenDetailsComponent
import com.tangem.features.markets.details.MarketsTokenDetailsComponent.Params
@@ -33,6 +34,7 @@ internal class DefaultMarketsTokenDetailsComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted params: Params,
analyticsEventHandler: AnalyticsEventHandler,
+ private val accountsFeatureToggles: AccountsFeatureToggles,
portfolioComponentFactory: MarketsPortfolioComponent.Factory,
) : AppComponentContext by appComponentContext, MarketsTokenDetailsComponent {
@@ -114,6 +116,7 @@ internal class DefaultMarketsTokenDetailsComponent @AssistedInject constructor(
onBackClick = ::navigateBack,
backButtonEnabled = bsState == BottomSheetState.EXPANDED,
onHeaderSizeChange = onHeaderSizeChange,
+ isAccountEnabled = accountsFeatureToggles.isFeatureEnabled,
portfolioBlock = portfolioComponent?.let { component ->
{ blockModifier ->
component.Content(blockModifier)
@@ -141,6 +144,7 @@ internal class DefaultMarketsTokenDetailsComponent @AssistedInject constructor(
onBackClick = ::navigateBack,
backButtonEnabled = true,
onHeaderSizeChange = {},
+ isAccountEnabled = accountsFeatureToggles.isFeatureEnabled,
portfolioBlock = portfolioComponent?.let { component ->
{ blockModifier ->
component.Content(blockModifier)
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/MarketsTokenDetailsContent.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/MarketsTokenDetailsContent.kt
index 7ffb94a1db..311f6709fe 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/MarketsTokenDetailsContent.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/MarketsTokenDetailsContent.kt
@@ -58,6 +58,7 @@ internal fun MarketsTokenDetailsContent(
onBackClick: () -> Unit,
onHeaderSizeChange: (Dp) -> Unit,
backButtonEnabled: Boolean,
+ isAccountEnabled: Boolean,
modifier: Modifier = Modifier,
portfolioBlock: @Composable ((Modifier) -> Unit)?,
) {
@@ -69,6 +70,7 @@ internal fun MarketsTokenDetailsContent(
onHeaderSizeChange = onHeaderSizeChange,
backButtonEnabled = backButtonEnabled,
portfolioBlock = portfolioBlock,
+ isAccountEnabled = isAccountEnabled,
addTopBarStatusBarInsets = addTopBarStatusBarPadding,
)
@@ -88,6 +90,7 @@ private fun Content(
onBackClick: () -> Unit,
onHeaderSizeChange: (Dp) -> Unit,
backButtonEnabled: Boolean,
+ isAccountEnabled: Boolean,
modifier: Modifier = Modifier,
portfolioBlock: @Composable ((Modifier) -> Unit)?,
) {
@@ -153,6 +156,7 @@ private fun Content(
tokenMarketDetailsBody(
state = state.body,
+ isAccountEnabled = isAccountEnabled,
portfolioBlock = portfolioBlock,
)
}
@@ -348,6 +352,7 @@ private fun Preview() {
backgroundColor = TangemTheme.colors.background.tertiary,
portfolioBlock = {},
backButtonEnabled = true,
+ isAccountEnabled = true,
addTopBarStatusBarPadding = false,
)
}
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/TokenMarketDetailsBody.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/TokenMarketDetailsBody.kt
index c2fb267ffe..c94c78f605 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/TokenMarketDetailsBody.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/TokenMarketDetailsBody.kt
@@ -4,17 +4,21 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import com.tangem.core.ui.res.TangemTheme
-import com.tangem.features.markets.details.impl.ui.state.MarketsTokenDetailsUM
import com.tangem.core.ui.components.UnableToLoadData
import com.tangem.core.ui.components.items.DescriptionItem
import com.tangem.core.ui.components.items.DescriptionPlaceholder
+import com.tangem.core.ui.extensions.stringResourceSafe
+import com.tangem.core.ui.res.TangemTheme
+import com.tangem.features.markets.details.impl.ui.state.MarketsTokenDetailsUM
+import com.tangem.features.markets.impl.R
internal fun LazyListScope.tokenMarketDetailsBody(
state: MarketsTokenDetailsUM.Body,
+ isAccountEnabled: Boolean,
portfolioBlock: @Composable ((Modifier) -> Unit)?,
) {
when (state) {
@@ -29,6 +33,10 @@ internal fun LazyListScope.tokenMarketDetailsBody(
}
}
+ if (isAccountEnabled) {
+ aboutCoinHeader()
+ }
+
loadingInfoBlocks()
}
is MarketsTokenDetailsUM.Body.Content -> {
@@ -42,6 +50,10 @@ internal fun LazyListScope.tokenMarketDetailsBody(
}
}
+ if (isAccountEnabled) {
+ aboutCoinHeader()
+ }
+
infoBlocksList(state.infoBlocks)
}
is MarketsTokenDetailsUM.Body.Error -> {
@@ -69,6 +81,21 @@ private fun LazyListScope.error(state: MarketsTokenDetailsUM.Body.Error) {
}
}
+private fun LazyListScope.aboutCoinHeader() {
+ item("aboutCoinHeader") {
+ Text(
+ modifier = Modifier.padding(
+ start = TangemTheme.dimens.spacing16,
+ end = TangemTheme.dimens.spacing16,
+ bottom = TangemTheme.dimens.spacing20,
+ ),
+ text = stringResourceSafe(R.string.markets_about_coin_header),
+ color = TangemTheme.colors.text.primary1,
+ style = TangemTheme.typography.h3,
+ )
+ }
+}
+
private fun LazyListScope.description(description: MarketsTokenDetailsUM.Description) {
item("description") {
DescriptionItem(
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/DefaultMarketsPortfolioComponent.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/DefaultMarketsPortfolioComponent.kt
index 8f3e1d6da7..da8bdbd11a 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/DefaultMarketsPortfolioComponent.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/DefaultMarketsPortfolioComponent.kt
@@ -14,9 +14,10 @@ import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.domain.markets.TokenMarketInfo
-import com.tangem.domain.models.TokenReceiveConfig
+import com.tangem.features.markets.portfolio.add.api.AddToPortfolioComponent
import com.tangem.features.markets.portfolio.api.MarketsPortfolioComponent
import com.tangem.features.markets.portfolio.impl.model.MarketsPortfolioModel
+import com.tangem.features.markets.portfolio.impl.model.MarketsPortfolioRoute
import com.tangem.features.markets.portfolio.impl.ui.MyPortfolio
import com.tangem.features.tokenreceive.TokenReceiveComponent
import dagger.assisted.Assisted
@@ -28,19 +29,25 @@ internal class DefaultMarketsPortfolioComponent @AssistedInject constructor(
@Assisted context: AppComponentContext,
@Assisted private val params: MarketsPortfolioComponent.Params,
private val tokenReceiveComponentFactory: TokenReceiveComponent.Factory,
+ private val addToPortfolioComponentFactory: AddToPortfolioComponent.Factory,
) : AppComponentContext by context, MarketsPortfolioComponent {
private val model: MarketsPortfolioModel = getOrCreateModel(params)
private val bottomSheetSlot = childSlot(
source = model.bottomSheetNavigation,
- serializer = TokenReceiveConfig.serializer(),
+ serializer = MarketsPortfolioRoute.serializer(),
handleBackButton = false,
childFactory = ::bottomSheetChild,
)
- override fun setTokenNetworks(networks: List) = model.setTokenNetworks(networks)
- override fun setNoNetworksAvailable() = model.setNoNetworksAvailable()
+ override fun setTokenNetworks(networks: List) {
+ model.setTokenNetworks(networks)
+ }
+
+ override fun setNoNetworksAvailable() {
+ model.setNoNetworksAvailable()
+ }
@Composable
override fun Content(modifier: Modifier) {
@@ -52,15 +59,24 @@ internal class DefaultMarketsPortfolioComponent @AssistedInject constructor(
}
private fun bottomSheetChild(
- config: TokenReceiveConfig,
+ config: MarketsPortfolioRoute,
componentContext: ComponentContext,
- ): ComposableBottomSheetComponent = tokenReceiveComponentFactory.create(
- context = childByContext(componentContext),
- params = TokenReceiveComponent.Params(
- config = config,
- onDismiss = model.bottomSheetNavigation::dismiss,
- ),
- )
+ ): ComposableBottomSheetComponent = when (config) {
+ MarketsPortfolioRoute.AddToPortfolio -> addToPortfolioComponentFactory.create(
+ context = childByContext(componentContext),
+ params = AddToPortfolioComponent.Params(
+ addToPortfolioManager = model.newAddToPortfolioManager!!,
+ callback = model.addToPortfolioCallback,
+ ),
+ )
+ is MarketsPortfolioRoute.TokenReceive -> tokenReceiveComponentFactory.create(
+ context = childByContext(componentContext),
+ params = TokenReceiveComponent.Params(
+ config = config.config,
+ onDismiss = model.bottomSheetNavigation::dismiss,
+ ),
+ )
+ }
@AssistedFactory
interface Factory : MarketsPortfolioComponent.Factory {
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioModel.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioModel.kt
index 3b1183b867..d5e434ad2a 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioModel.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioModel.kt
@@ -4,6 +4,7 @@ import androidx.compose.runtime.Stable
import arrow.core.getOrElse
import com.arkivanov.decompose.router.slot.SlotNavigation
import com.arkivanov.decompose.router.slot.activate
+import com.arkivanov.decompose.router.slot.dismiss
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
@@ -16,15 +17,14 @@ import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.SnackbarMessage
+import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.managetokens.CheckCurrencyUnsupportedUseCase
import com.tangem.domain.managetokens.model.CurrencyUnsupportedState
import com.tangem.domain.markets.SaveMarketTokensUseCase
import com.tangem.domain.markets.TokenMarketInfo
-import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.currency.CryptoCurrency
-import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isMultiCurrency
@@ -32,11 +32,13 @@ import com.tangem.domain.transaction.usecase.ReceiveAddressesFactory
import com.tangem.domain.wallets.usecase.ColdWalletAndHasMissedDerivationsUseCase
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
import com.tangem.features.markets.impl.R
+import com.tangem.features.markets.portfolio.add.api.AddToPortfolioComponent
import com.tangem.features.markets.portfolio.api.MarketsPortfolioComponent
import com.tangem.features.markets.portfolio.impl.analytics.PortfolioAnalyticsEvent
import com.tangem.features.markets.portfolio.impl.loader.PortfolioData
import com.tangem.features.markets.portfolio.impl.loader.PortfolioDataLoader
import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM
+import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM.Tokens.AddButtonState
import com.tangem.features.markets.portfolio.impl.ui.state.TokenActionsBSContentUM
import com.tangem.features.tokenreceive.TokenReceiveFeatureToggle
import com.tangem.features.wallet.utils.UserWalletImageFetcher
@@ -49,6 +51,7 @@ import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
+import com.tangem.features.markets.portfolio.add.api.AddToPortfolioManager as NewAddToPortfolioManager
@Suppress("LongParameterList", "LargeClass")
@Stable
@@ -69,6 +72,9 @@ internal class MarketsPortfolioModel @Inject constructor(
private val tokenReceiveFeatureToggle: TokenReceiveFeatureToggle,
private val userWalletImageFetcher: UserWalletImageFetcher,
private val receiveAddressesFactory: ReceiveAddressesFactory,
+ private val accountsFeatureToggles: AccountsFeatureToggles,
+ newAddToPortfolioManagerFactory: NewAddToPortfolioManager.Factory,
+ private val newMarketsPortfolioDelegateFactory: NewMarketsPortfolioDelegate.Factory,
) : Model() {
val state: StateFlow get() = _state
@@ -80,12 +86,40 @@ internal class MarketsPortfolioModel @Inject constructor(
source = params.analyticsParams?.source,
)
+ val newAddToPortfolioManager: NewAddToPortfolioManager?
+ val newMarketsPortfolioDelegate: NewMarketsPortfolioDelegate?
+
/** Multi-wallet [UserWalletId] that user uses to add new tokens in AddToPortfolio bottom sheet */
private val selectedMultiWalletIdFlow = MutableStateFlow(value = null)
private val portfolioBSVisibilityModelFlow = MutableStateFlow(value = PortfolioBSVisibilityModel())
- val bottomSheetNavigation: SlotNavigation = SlotNavigation()
+ val bottomSheetNavigation: SlotNavigation = SlotNavigation()
+ val addToPortfolioCallback = object : AddToPortfolioComponent.Callback {
+ override fun onDismiss() = bottomSheetNavigation.dismiss()
+ }
+
+ private val tokenActionsHandler = tokenActionsIntentsFactory.create(
+ currentAppCurrency = Provider { currentAppCurrency.value },
+ updateTokenReceiveBSConfig = { updateBlock ->
+ if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled.not()) {
+ updateTokensState {
+ it.copy(tokenReceiveBSConfig = updateBlock(it.tokenReceiveBSConfig))
+ }
+ }
+ },
+ onHandleQuickAction = { handledAction ->
+ analyticsEventHandler.send(
+ analyticsEventBuilder.quickActionClick(
+ actionUM = handledAction.action,
+ blockchainName = handledAction.cryptoCurrencyData.status.currency.network.name,
+ ),
+ )
+ if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled) {
+ configureReceiveAddresses(handledAction)
+ }
+ },
+ )
private val currentAppCurrency = getSelectedAppCurrencyUseCase()
.map { maybeAppCurrency ->
@@ -132,27 +166,7 @@ internal class MarketsPortfolioModel @Inject constructor(
},
),
currentState = Provider { _state.value },
- tokenActionsHandler = tokenActionsIntentsFactory.create(
- currentAppCurrency = Provider { currentAppCurrency.value },
- updateTokenReceiveBSConfig = { updateBlock ->
- if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled.not()) {
- updateTokensState {
- it.copy(tokenReceiveBSConfig = updateBlock(it.tokenReceiveBSConfig))
- }
- }
- },
- onHandleQuickAction = { handledAction ->
- analyticsEventHandler.send(
- analyticsEventBuilder.quickActionClick(
- actionUM = handledAction.action,
- blockchainName = handledAction.cryptoCurrencyData.status.currency.network.name,
- ),
- )
- if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled) {
- configureReceiveAddresses(handledAction)
- }
- },
- ),
+ tokenActionsHandler = tokenActionsHandler,
updateTokens = { updateBlock ->
updateTokensState { state ->
state.copy(tokens = updateBlock(state.tokens))
@@ -161,18 +175,50 @@ internal class MarketsPortfolioModel @Inject constructor(
)
init {
- // Subscribe on selected wallet flow to support actual selected wallet
- subscribeOnSelectedMultiWalletUpdates()
+ if (accountsFeatureToggles.isFeatureEnabled) {
+ newAddToPortfolioManager = newAddToPortfolioManagerFactory
+ .create(
+ modelScope,
+ params.token,
+ params.analyticsParams,
+ )
+ newMarketsPortfolioDelegate = newMarketsPortfolioDelegateFactory.create(
+ scope = modelScope,
+ token = params.token,
+ tokenActionsHandler = tokenActionsHandler,
+ buttonState = newAddToPortfolioManager.state.map {
+ when (it) {
+ is NewAddToPortfolioManager.State.AvailableToAdd -> AddButtonState.Available
+ NewAddToPortfolioManager.State.Init -> AddButtonState.Loading
+ NewAddToPortfolioManager.State.NothingToAdd -> AddButtonState.Unavailable
+ }
+ },
+ onAddClick = { bottomSheetNavigation.activate(MarketsPortfolioRoute.AddToPortfolio) },
+ )
+ newMarketsPortfolioDelegate.combineData()
+ .onEach { _state.value = it }
+ .flowOn(dispatchers.default)
+ .launchIn(modelScope)
+ } else {
+ newAddToPortfolioManager = null
+ newMarketsPortfolioDelegate = null
+ // Subscribe on selected wallet flow to support actual selected wallet
+ subscribeOnSelectedMultiWalletUpdates()
- subscribeOnStateUpdates()
+ subscribeOnStateUpdates()
+ }
}
fun setTokenNetworks(networks: List) {
addToPortfolioManager.setAvailableNetworks(networks)
+ newAddToPortfolioManager?.setTokenNetworks(networks)
+ newMarketsPortfolioDelegate?.setTokenNetworks(networks)
}
fun setNoNetworksAvailable() {
addToPortfolioManager.setAvailableNetworks(emptyList())
+ newAddToPortfolioManager?.setTokenNetworks(emptyList())
+ newMarketsPortfolioDelegate?.setTokenNetworks(emptyList())
}
private fun subscribeOnSelectedMultiWalletUpdates() {
@@ -378,7 +424,7 @@ internal class MarketsPortfolioModel @Inject constructor(
status = quickAction.cryptoCurrencyData.status,
userWalletId = quickAction.cryptoCurrencyData.userWallet.walletId,
) ?: return@launch
- bottomSheetNavigation.activate(tokenConfig)
+ bottomSheetNavigation.activate(MarketsPortfolioRoute.TokenReceive(tokenConfig))
}
}
}
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioRoute.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioRoute.kt
new file mode 100644
index 0000000000..576d9cda78
--- /dev/null
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioRoute.kt
@@ -0,0 +1,17 @@
+package com.tangem.features.markets.portfolio.impl.model
+
+import com.tangem.core.decompose.navigation.Route
+import com.tangem.domain.models.TokenReceiveConfig
+import kotlinx.serialization.Serializable
+
+@Serializable
+sealed interface MarketsPortfolioRoute : Route {
+
+ @Serializable
+ data object AddToPortfolio : MarketsPortfolioRoute
+
+ @Serializable
+ data class TokenReceive(
+ val config: TokenReceiveConfig,
+ ) : MarketsPortfolioRoute
+}
\ No newline at end of file
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/NewMarketsPortfolioDelegate.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/NewMarketsPortfolioDelegate.kt
new file mode 100644
index 0000000000..20dd17c10c
--- /dev/null
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/NewMarketsPortfolioDelegate.kt
@@ -0,0 +1,325 @@
+package com.tangem.features.markets.portfolio.impl.model
+
+import com.tangem.common.ui.account.AccountTitleUM
+import com.tangem.common.ui.account.toUM
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
+import com.tangem.core.ui.extensions.TextReference
+import com.tangem.core.ui.extensions.stringReference
+import com.tangem.domain.account.models.AccountStatusList
+import com.tangem.domain.account.status.supplier.MultiAccountStatusListSupplier
+import com.tangem.domain.account.status.usecase.GetCryptoCurrencyActionsUseCaseV2
+import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
+import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
+import com.tangem.domain.appcurrency.model.AppCurrency
+import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
+import com.tangem.domain.markets.TokenMarketInfo
+import com.tangem.domain.markets.TokenMarketParams
+import com.tangem.domain.models.account.Account
+import com.tangem.domain.models.account.AccountStatus
+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.models.wallet.UserWalletId
+import com.tangem.domain.models.wallet.isMultiCurrency
+import com.tangem.domain.tokens.model.TokenActionsState
+import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
+import com.tangem.features.markets.portfolio.impl.loader.PortfolioData
+import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM
+import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM.Tokens.AddButtonState
+import com.tangem.features.markets.portfolio.impl.ui.state.PortfolioHeader
+import com.tangem.features.markets.portfolio.impl.ui.state.PortfolioListItem
+import com.tangem.features.markets.portfolio.impl.ui.state.WalletHeader
+import com.tangem.utils.extensions.isZero
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.collections.immutable.toImmutableList
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.*
+
+@Suppress("LongParameterList")
+internal class NewMarketsPortfolioDelegate @AssistedInject constructor(
+ private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
+ private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
+ private val allAccountSupplier: MultiAccountStatusListSupplier,
+ private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCaseV2,
+ private val getUserWalletUseCase: GetUserWalletUseCase,
+ private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
+ @Assisted private val scope: CoroutineScope,
+ @Assisted private val token: TokenMarketParams,
+ @Assisted private val tokenActionsHandler: TokenActionsHandler,
+ @Assisted private val buttonState: Flow,
+ @Assisted private val onAddClick: () -> Unit,
+) {
+
+ private val currencyRawId: CryptoCurrency.RawID = token.id
+ private var expandedHolder: MutableStateFlow>>? = null
+
+ private val settingsFlow: Flow = combine(
+ flow = getSelectedAppCurrencyUseCase.invokeOrDefault(),
+ flow2 = getBalanceHidingSettingsUseCase.isBalanceHidden(),
+ flow3 = isAccountsModeEnabledUseCase(),
+ transform = ::SettingsBox,
+ ).shareIn(
+ replay = 1,
+ started = SharingStarted.Eagerly,
+ scope = scope,
+ ).distinctUntilChanged()
+
+ private val availableNetworks = MutableSharedFlow>(
+ replay = 1,
+ onBufferOverflow = BufferOverflow.DROP_OLDEST,
+ )
+
+ fun setTokenNetworks(networks: List) {
+ availableNetworks.tryEmit(networks)
+ }
+
+ fun combineData(): Flow {
+ return availableNetworks.transformLatest { availableNetworks ->
+ when {
+ availableNetworks.isEmpty() -> emit(MyPortfolioUM.Unavailable)
+ else -> emitAll(onAvailableNetworksFlow().distinctUntilChanged())
+ }
+ }.distinctUntilChanged()
+ }
+
+ private fun onAvailableNetworksFlow(): Flow =
+ portfolioWithThisCurrencyFLow().transformLatest { portfolioWithCurrency ->
+ fun addFirstTokenUM() = MyPortfolioUM.AddFirstToken(
+ onAddClick = onAddClick,
+ addToPortfolioBSConfig = TangemBottomSheetConfig.Empty,
+ )
+ when (portfolioWithCurrency.flattenAddedCurrency.isEmpty()) {
+ false -> emitAll(contentFlow(portfolioWithCurrency).distinctUntilChanged())
+ true -> when (portfolioWithCurrency.hasMultiWallets) {
+ true -> emit(addFirstTokenUM())
+ false -> emit(MyPortfolioUM.UnavailableForWallet)
+ }
+ }
+ }
+
+ private fun contentFlow(portfolio: PortfoliosWithThisCurrency): Flow {
+ fun Portfolio.actionsFoAccountCurrencies(): List>> =
+ accountsWithAdded.map { account ->
+ fun CryptoCurrencyStatus.actionsFlow() = getCryptoCurrencyActionsUseCase(
+ accountId = account.accountStatus.account.accountId,
+ currency = this.currency,
+ ).map { actionsState -> actionsState.cryptoCurrencyStatus.currency to actionsState }
+ account.addedCurrency.map { it.actionsFlow() }
+ }.flatten()
+
+ val allAddedTokenActions =
+ portfolio.portfolios.map { portfolio -> portfolio.actionsFoAccountCurrencies() }.flatten()
+
+ return combine(
+ flow = combine(allAddedTokenActions) { it.toMap() }.distinctUntilChanged(),
+ flow2 = buttonState.distinctUntilChanged(),
+ flow3 = getExpandedHolder(portfolio),
+ flow4 = settingsFlow.distinctUntilChanged(),
+ transform = { actions, addButtonState, expanded, settings ->
+ buildContentState(
+ portfolio = portfolio,
+ allActions = actions,
+ addButtonState = addButtonState,
+ expanded = expanded,
+ settings = settings,
+ )
+ },
+ )
+ }
+
+ private fun getExpandedHolder(
+ portfolio: PortfoliosWithThisCurrency,
+ ): StateFlow>> {
+ val expandedHolder = this.expandedHolder
+ if (expandedHolder != null) return expandedHolder
+ val allAddedCurrency = portfolio.flattenAddedCurrency
+ val shouldForceExpand = allAddedCurrency.size == 1 &&
+ allAddedCurrency.first().value.amount?.isZero() == true
+
+ val initValue = when {
+ shouldForceExpand -> {
+ val currency = allAddedCurrency.first()
+ // find userWallet than have this single added token
+ portfolio.portfolios
+ .find { it.accountsWithAdded.find { account -> account.addedCurrency.isNotEmpty() } != null }
+ ?.userWallet
+ ?.let { setOf(it.walletId to currency.currency.id) }
+ ?: setOf()
+ }
+ else -> setOf()
+ }
+ return MutableStateFlow(initValue)
+ .also { this.expandedHolder = it }
+ }
+
+ private fun portfolioWithThisCurrencyFLow(): Flow =
+ allAccountSupplier(Unit).map { list -> list.map { it.addedAccountsFlow() } }.flatMapLatest { flows ->
+ combine(flows) {
+ PortfoliosWithThisCurrency(
+ currencyRawId = currencyRawId,
+ portfolios = it.toList(),
+ )
+ }
+ }.distinctUntilChanged()
+
+ private fun AccountStatusList.addedAccountsFlow(): Flow =
+ getUserWalletUseCase.invokeFlow(this.userWalletId).mapNotNull { it.getOrNull() }.map { wallet ->
+ Portfolio(
+ userWallet = wallet,
+ accountStatusList = this,
+ accountsWithAdded = this.filterByRawID(),
+ )
+ }.distinctUntilChanged()
+
+ private fun AccountStatusList.filterByRawID(): List {
+ fun AccountStatus.filterByRawID(): List = when (this) {
+ is AccountStatus.CryptoPortfolio -> this.tokenList.flattenCurrencies()
+ .filter { status -> status.currency.id.rawCurrencyId == currencyRawId }
+ }
+ return accountStatuses.map { accountStatus ->
+ AccountWithAdded(
+ accountStatus = accountStatus,
+ addedCurrency = accountStatus.filterByRawID(),
+ )
+ }
+ }
+
+ private fun buildContentState(
+ portfolio: PortfoliosWithThisCurrency,
+ allActions: Map,
+ addButtonState: AddButtonState,
+ expanded: Set>,
+ settings: SettingsBox,
+ ): MyPortfolioUM.Content {
+ val appCurrency = settings.appCurrency
+ val isBalanceHidden = settings.isBalanceHidden
+ val isAccountMode = settings.isAccountMode
+ val uiItems: MutableList = mutableListOf()
+
+ fun toggleQuickActions(key: Pair) = expandedHolder?.update { expanded ->
+ val isExpand = expanded.contains(key)
+ if (isExpand) expanded.minus(key) else expanded.plus(key)
+ }
+
+ val tokenUMConverter = PortfolioTokenUMConverter(
+ appCurrency = appCurrency,
+ isBalanceHidden = isBalanceHidden,
+ onTokenItemClick = { },
+ tokenActionsHandler = tokenActionsHandler,
+ )
+
+ portfolio.portfolios.forEach { portfolioItem ->
+ if (portfolioItem.flattenAddedCurrency.isEmpty()) return@forEach
+ val userWallet = portfolioItem.userWallet
+ if (isAccountMode) {
+ uiItems.add(portfolioItem.userWallet.toWalletHeader())
+ } else {
+ uiItems.add(portfolioItem.userWallet.toWalletPortfolioHeader())
+ }
+
+ portfolioItem.accountsWithAdded.forEach { accountWithAdded ->
+ if (accountWithAdded.addedCurrency.isEmpty()) return@forEach
+ if (isAccountMode) {
+ val account = accountWithAdded.accountStatus.account
+ uiItems.add(account.toAccountPortfolioHeader())
+ }
+
+ accountWithAdded.addedCurrency.forEach { currencyStatus ->
+ val actions = allActions[currencyStatus.currency]?.states
+ ?: emptyList()
+ val value = PortfolioData.CryptoCurrencyData(
+ userWallet = userWallet,
+ status = currencyStatus,
+ actions = actions,
+ )
+ val expandedKey = portfolioItem.userWallet.walletId to currencyStatus.currency.id
+ val isExpand = expanded.contains(expandedKey)
+
+ val tokenItem = tokenUMConverter.convertV2(
+ onTokenItemClick = { wallet, status ->
+ toggleQuickActions(wallet.walletId to status.currency.id)
+ },
+ value = value,
+ isQuickActionsShown = isExpand,
+ )
+ uiItems.add(tokenItem)
+ }
+ }
+ }
+
+ return MyPortfolioUM.Content(
+ items = uiItems.toImmutableList(),
+ buttonState = addButtonState,
+ onAddClick = onAddClick,
+ )
+ }
+
+ private fun Account.toAccountPortfolioHeader(): PortfolioHeader = PortfolioHeader(
+ id = this.accountId.value,
+ state = AccountTitleUM.Account(
+ prefixText = TextReference.EMPTY,
+ name = this.accountName.toUM().value,
+ icon = when (this) {
+ is Account.CryptoPortfolio -> this.icon.toUM()
+ },
+ ),
+ )
+
+ private fun UserWallet.toWalletPortfolioHeader(): PortfolioHeader = PortfolioHeader(
+ id = this.walletId.stringValue,
+ state = AccountTitleUM.Text(
+ title = stringReference(this.name),
+ ),
+ )
+
+ private fun UserWallet.toWalletHeader(): WalletHeader = WalletHeader(
+ id = this.walletId.stringValue,
+ name = stringReference(this.name),
+ )
+
+ @Suppress("LongParameterList")
+ @AssistedFactory
+ interface Factory {
+ fun create(
+ scope: CoroutineScope,
+ token: TokenMarketParams,
+ tokenActionsHandler: TokenActionsHandler,
+ buttonState: Flow,
+ onAddClick: () -> Unit,
+ ): NewMarketsPortfolioDelegate
+ }
+}
+
+private data class PortfoliosWithThisCurrency(
+ val currencyRawId: CryptoCurrency.RawID,
+ val portfolios: List,
+) {
+
+ val hasMultiWallets: Boolean = portfolios.any { it.userWallet.isMultiCurrency }
+
+ val flattenAddedCurrency: List =
+ portfolios.map { portfolio -> portfolio.flattenAddedCurrency }.flatten()
+}
+
+private data class Portfolio(
+ val userWallet: UserWallet,
+ val accountStatusList: AccountStatusList,
+ val accountsWithAdded: List,
+) {
+ val flattenAddedCurrency: List =
+ accountsWithAdded.map { it.addedCurrency }.flatten()
+}
+
+private data class AccountWithAdded(
+ val addedCurrency: List,
+ val accountStatus: AccountStatus,
+)
+
+private data class SettingsBox(
+ val appCurrency: AppCurrency,
+ val isBalanceHidden: Boolean,
+ val isAccountMode: Boolean,
+)
\ No newline at end of file
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/PortfolioTokenUMConverter.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/PortfolioTokenUMConverter.kt
index 2bb27f2a78..0467a2e2b3 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/PortfolioTokenUMConverter.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/PortfolioTokenUMConverter.kt
@@ -27,6 +27,24 @@ internal class PortfolioTokenUMConverter(
private val tokenActionsHandler: TokenActionsHandler,
) : Converter {
+ fun convertV2(
+ value: PortfolioData.CryptoCurrencyData,
+ isQuickActionsShown: Boolean,
+ onTokenItemClick: (UserWallet, CryptoCurrencyStatus) -> Unit,
+ ): PortfolioTokenUM {
+ val tokenItemStateConverter = TokenItemStateConverter(
+ appCurrency = appCurrency,
+ onItemClick = { _, status -> onTokenItemClick(value.userWallet, status) },
+ )
+ return PortfolioTokenUM(
+ tokenItemState = tokenItemStateConverter.convert(value = value.status),
+ walletId = value.userWallet.walletId,
+ isBalanceHidden = isBalanceHidden,
+ isQuickActionsShown = isQuickActionsShown,
+ quickActions = quickActions(cryptoData = value, tokenActionsHandler = tokenActionsHandler),
+ )
+ }
+
override fun convert(value: PortfolioData.CryptoCurrencyData): PortfolioTokenUM {
val tokenItemStateConverter = TokenItemStateConverter(
appCurrency = appCurrency,
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/MyPortfolio.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/MyPortfolio.kt
index 1c88861dac..582bcc4690 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/MyPortfolio.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/MyPortfolio.kt
@@ -10,29 +10,43 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.key
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
+import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEachIndexed
+import com.tangem.common.ui.account.AccountTitle
+import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheet
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.SmallButtonShimmer
import com.tangem.core.ui.components.TextShimmer
import com.tangem.core.ui.components.block.information.InformationBlock
-import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheet
import com.tangem.core.ui.components.buttons.SecondarySmallButton
import com.tangem.core.ui.components.buttons.SmallButtonConfig
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
+import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.markets.impl.R
import com.tangem.features.markets.portfolio.impl.ui.preview.PreviewMyPortfolioUMProvider
-import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM
+import com.tangem.features.markets.portfolio.impl.ui.state.*
import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM.Tokens.AddButtonState
@Composable
internal fun MyPortfolio(state: MyPortfolioUM, modifier: Modifier = Modifier) {
+ if (state is MyPortfolioUM.Content) {
+ val contentModifier = Modifier.padding(
+ start = TangemTheme.dimens.spacing16,
+ top = TangemTheme.dimens.spacing20,
+ end = TangemTheme.dimens.spacing16,
+ bottom = TangemTheme.dimens.spacing32,
+ )
+ PortfolioList(state, contentModifier)
+ return
+ }
InformationBlock(
modifier = modifier,
contentHorizontalPadding = TangemTheme.dimens.spacing0,
@@ -55,6 +69,7 @@ internal fun MyPortfolio(state: MyPortfolioUM, modifier: Modifier = Modifier) {
MyPortfolioUM.Loading -> LoadingPlaceholder(modifier = contentModifier)
MyPortfolioUM.Unavailable -> UnavailableAsset(modifier = contentModifier)
MyPortfolioUM.UnavailableForWallet -> UnavailableAssetForWallet(modifier = contentModifier)
+ is MyPortfolioUM.Content -> PortfolioList(state = state)
}
}
@@ -119,6 +134,7 @@ private fun TokenList(state: MyPortfolioUM.Tokens, modifier: Modifier = Modifier
state.tokens.fastForEachIndexed { index, token ->
key(token.tokenItemState.id) {
PortfolioItem(
+ modifier = Modifier.background(color = TangemTheme.colors.background.action),
state = token,
lastInList = index == state.tokens.size - 1,
)
@@ -129,6 +145,111 @@ private fun TokenList(state: MyPortfolioUM.Tokens, modifier: Modifier = Modifier
TokenReceiveBottomSheet(config = state.tokenReceiveBSConfig)
}
+@Composable
+private fun PortfolioList(state: MyPortfolioUM.Content, modifier: Modifier = Modifier) {
+ Column(modifier) {
+ key("PortfolioListHeader") {
+ Row(
+ modifier = Modifier.padding(horizontal = 4.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Text(
+ modifier = Modifier.weight(1f),
+ text = stringResourceSafe(R.string.markets_common_my_portfolio),
+ style = TangemTheme.typography.h3,
+ color = TangemTheme.colors.text.primary1,
+ )
+ AddButton(state = state.buttonState, onClick = state.onAddClick)
+ }
+ }
+
+ state.items.fastForEachIndexed { index, item ->
+ val previousItem = state.items.getOrNull(index.dec())
+ val nextItem = state.items.getOrNull(index.inc())
+ val itemModifier = Modifier
+ .fillMaxWidth()
+ .getOffsetModifier(item, previousItem)
+ .getBackgroundModifier(item, previousItem, nextItem)
+
+ key(item.id) {
+ PortfolioItem(
+ item = item,
+ modifier = itemModifier,
+ lastInList = index == state.items.size - 1,
+ )
+ }
+ }
+ }
+}
+
+@Composable
+private fun Modifier.getBackgroundModifier(
+ item: PortfolioListItem,
+ previousItem: PortfolioListItem?,
+ nextItem: PortfolioListItem?,
+): Modifier {
+ val color = TangemTheme.colors.background.action
+ val radius = 14.dp
+ val topRound = RoundedCornerShape(topStart = radius, topEnd = radius)
+ val bottomRound = RoundedCornerShape(bottomStart = radius, bottomEnd = radius)
+ val allRound = RoundedCornerShape(size = radius)
+ val backgroundModifier = when (item) {
+ is WalletHeader -> this
+ is PortfolioHeader -> this
+ .clip(topRound)
+ .background(color = color)
+ is PortfolioTokenUM -> when {
+ previousItem is PortfolioHeader && nextItem !is PortfolioTokenUM -> this
+ .clip(bottomRound)
+ .background(color = color)
+ previousItem is WalletHeader && nextItem !is PortfolioTokenUM -> this
+ .clip(allRound)
+ .background(color = color)
+ previousItem is PortfolioTokenUM && nextItem !is PortfolioTokenUM -> this
+ .clip(bottomRound)
+ .background(color = color)
+ else -> this.background(color = color)
+ }
+ }
+ return backgroundModifier
+}
+
+private fun Modifier.getOffsetModifier(item: PortfolioListItem, previousItem: PortfolioListItem?): Modifier = when {
+ item is WalletHeader -> this.padding(top = 20.dp, start = 4.dp, end = 4.dp)
+ item is PortfolioHeader && previousItem is PortfolioTokenUM -> this.padding(top = 12.dp)
+ item is PortfolioHeader && previousItem == null -> this.padding(top = 20.dp)
+ previousItem is WalletHeader -> this.padding(top = 12.dp)
+ previousItem is PortfolioTokenUM -> this
+ else -> this
+}
+
+@Composable
+private fun PortfolioItem(item: PortfolioListItem, lastInList: Boolean, modifier: Modifier = Modifier) {
+ when (item) {
+ is PortfolioHeader -> AccountTitle(
+ modifier = modifier.padding(
+ start = 12.dp,
+ top = 12.dp,
+ bottom = 8.dp,
+ ),
+ accountTitleUM = item.state,
+ textStyle = TangemTheme.typography.caption1,
+ textColor = TangemTheme.colors.text.primary1,
+ )
+ is PortfolioTokenUM -> PortfolioItem(
+ state = item,
+ modifier = modifier,
+ lastInList = lastInList,
+ )
+ is WalletHeader -> Text(
+ modifier = modifier,
+ text = item.name.resolveReference(),
+ style = TangemTheme.typography.subtitle1,
+ color = TangemTheme.colors.text.primary1,
+ )
+ }
+}
+
@Composable
fun UnavailableAsset(modifier: Modifier = Modifier) {
UnavailableContent(
@@ -199,8 +320,7 @@ private fun Preview(@PreviewParameter(PreviewMyPortfolioUMProvider::class) state
TangemThemePreview {
Box(
modifier = Modifier
- .background(TangemTheme.colors.background.tertiary)
- .padding(TangemTheme.dimens.spacing8),
+ .background(TangemTheme.colors.background.tertiary),
) {
MyPortfolio(state)
}
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioItem.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioItem.kt
index ac68e930d6..4f47bfddab 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioItem.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioItem.kt
@@ -45,7 +45,6 @@ internal fun PortfolioItem(state: PortfolioTokenUM, lastInList: Boolean, modifie
TokenItem(
state = tokenItemState,
isBalanceHidden = state.isBalanceHidden,
- modifier = Modifier.background(color = TangemTheme.colors.background.action),
itemPaddingValues = PaddingValues(
start = TangemTheme.dimens.spacing10,
end = TangemTheme.dimens.spacing12,
@@ -54,7 +53,6 @@ internal fun PortfolioItem(state: PortfolioTokenUM, lastInList: Boolean, modifie
PortfolioQuickActions(
modifier = Modifier
- .background(color = TangemTheme.colors.background.action)
.padding(
bottom = if (lastInList) {
TangemTheme.dimens.spacing12
@@ -82,6 +80,7 @@ private fun Preview(@PreviewParameter(PortfolioTokenUMProvider::class) tokenUM:
}
PortfolioItem(
+ modifier = Modifier.background(color = TangemTheme.colors.background.action),
state = tokenUM.copy(
tokenItemState = when (tokenUM.tokenItemState) {
is TokenItemState.Content -> tokenUM.tokenItemState.copy(onItemClick = { onItemClick() })
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/preview/PreviewMyPortfolioUMProvider.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/preview/PreviewMyPortfolioUMProvider.kt
index fcf3ade8df..6f8a95f4fc 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/preview/PreviewMyPortfolioUMProvider.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/preview/PreviewMyPortfolioUMProvider.kt
@@ -1,15 +1,19 @@
package com.tangem.features.markets.portfolio.impl.ui.preview
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
+import com.tangem.common.ui.account.AccountIconPreviewData
+import com.tangem.common.ui.account.AccountTitleUM
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.token.state.TokenItemState
+import com.tangem.core.ui.components.token.state.TokenItemState.FiatAmountState
+import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.models.wallet.UserWalletId
-import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM
-import com.tangem.features.markets.portfolio.impl.ui.state.PortfolioTokenUM
-import com.tangem.features.markets.portfolio.impl.ui.state.QuickActionUM
+import com.tangem.features.markets.portfolio.impl.ui.state.*
+import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM.Tokens
import kotlinx.collections.immutable.persistentListOf
+import java.util.UUID
internal class PreviewMyPortfolioUMProvider : PreviewParameterProvider {
@@ -40,35 +44,107 @@ internal class PreviewMyPortfolioUMProvider : PreviewParameterProvider,
+ val buttonState: Tokens.AddButtonState,
+ val onAddClick: () -> Unit,
+ ) : MyPortfolioUM() {
+
+ override val addToPortfolioBSConfig: TangemBottomSheetConfig = TangemBottomSheetConfig.Empty
+ }
+
data class AddFirstToken(
override val addToPortfolioBSConfig: TangemBottomSheetConfig,
val onAddClick: () -> Unit,
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/PortfolioTokenUM.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/PortfolioTokenUM.kt
index dc16268b53..13e65fff09 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/PortfolioTokenUM.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/state/PortfolioTokenUM.kt
@@ -1,16 +1,33 @@
package com.tangem.features.markets.portfolio.impl.ui.state
+import com.tangem.common.ui.account.AccountTitleUM
import com.tangem.core.ui.components.token.state.TokenItemState
+import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.models.wallet.UserWalletId
import kotlinx.collections.immutable.ImmutableList
+internal sealed interface PortfolioListItem {
+ val id: String
+}
+
+internal data class WalletHeader(
+ override val id: String,
+ val name: TextReference,
+) : PortfolioListItem
+
+internal data class PortfolioHeader(
+ override val id: String,
+ val state: AccountTitleUM,
+) : PortfolioListItem
+
internal data class PortfolioTokenUM(
val tokenItemState: TokenItemState,
val walletId: UserWalletId,
val isBalanceHidden: Boolean,
val isQuickActionsShown: Boolean,
val quickActions: QuickActions,
-) {
+) : PortfolioListItem {
+ override val id: String = tokenItemState.id
data class QuickActions(
val actions: ImmutableList,