Updated on 2026-08-14

This commit is contained in:
Tangem 2026-06-30 18:38:15 +02:00
parent e0c6728675
commit 79431d6b0a
5 changed files with 150 additions and 70 deletions

View file

@ -1,19 +1,11 @@
package com.tangem.features.feed.components.market.details
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.LifecycleStartEffect
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.arkivanov.decompose.ComponentContext
@ -27,32 +19,24 @@ import com.tangem.core.decompose.context.child
import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.DesignFeatureToggles
import com.tangem.core.ui.R
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
import com.tangem.core.ui.components.haze.hazeEffectTangem
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.core.ui.decompose.ComposableModularBottomSheetContentComponent
import com.tangem.core.ui.ds.topbar.TangemTopBar
import com.tangem.core.ui.ds.topbar.TangemTopBarType
import com.tangem.core.ui.extensions.clickableSingle
import com.tangem.core.ui.res.LocalMainBottomSheetColor
import com.tangem.core.ui.res.LocalRedesignEnabled
import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.markets.PreselectedTokenDetailsSection
import com.tangem.domain.markets.TokenMarketParams
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.commonfeatures.api.managefunds.ManageFundsComponent
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioComponent
import com.tangem.features.commonfeatures.api.managefunds.ManageFundsComponent
import com.tangem.features.feed.components.market.details.portfolio.api.MarketsPortfolioComponent
import com.tangem.features.feed.components.market.details.portfolioblock.PortfolioBlockComponent
import com.tangem.features.feed.components.market.details.portfolioblock.PortfolioBlockParentClickIntents
import com.tangem.features.feed.model.market.details.MarketsTokenDetailsModel
import com.tangem.features.feed.model.market.details.analytics.MarketDetailsAnalyticsEvent
import com.tangem.features.feed.model.market.details.state.TokenNetworksState
import com.tangem.features.feed.ui.LocalIsOpenedInBottomSheet
import com.tangem.features.feed.ui.market.detailed.MarketsTokenDetailsContent
import com.tangem.features.feed.ui.market.detailed.MarketsTokenDetailsTopBar
import com.tangem.features.feed.ui.market.detailed.MarketsTokenDetailsTitle
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
@ -187,58 +171,12 @@ internal class DefaultMarketsTokenDetailsComponent(
@Composable
override fun Title(bottomSheetState: State<BottomSheetState>) {
val state by model.state.collectAsStateWithLifecycle()
val background = LocalMainBottomSheetColor.current.value
if (LocalRedesignEnabled.current) {
TangemTopBar(
startContent = {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_arrow_back_28),
contentDescription = null,
tint = TangemTheme.colors2.graphic.neutral.primary,
modifier = Modifier
.size(TangemTheme.dimens2.x11)
.clip(CircleShape)
.hazeEffectTangem { blurRadius = 8.dp }
.clickableSingle(
onClick = { params.onBackClicked() },
enabled = bottomSheetState.value == BottomSheetState.EXPANDED,
)
.padding(TangemTheme.dimens2.x2),
)
},
endContent = {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_share_new_24),
contentDescription = null,
tint = TangemTheme.colors2.graphic.neutral.primary,
modifier = Modifier
.size(TangemTheme.dimens2.x11)
.clip(CircleShape)
.hazeEffectTangem { blurRadius = 8.dp }
.clickableSingle(
onClick = state.onShareClick,
enabled = bottomSheetState.value == BottomSheetState.EXPANDED,
)
.padding(TangemTheme.dimens2.x2_5),
)
},
type = if (LocalIsOpenedInBottomSheet.current) {
TangemTopBarType.BottomSheet
} else {
TangemTopBarType.Default
},
)
} else {
MarketsTokenDetailsTopBar(
onBackClick = { params.onBackClicked() },
isBackButtonEnabled = bottomSheetState.value == BottomSheetState.EXPANDED,
shouldShowPriceSubtitle = state.shouldShowPriceSubtitle,
tokenName = state.tokenName,
tokenPrice = state.priceText,
backgroundColor = background,
onShareClick = state.onShareClick,
)
}
MarketsTokenDetailsTitle(
state = state,
backgroundColor = LocalMainBottomSheetColor.current.value,
isBackButtonEnabled = bottomSheetState.value == BottomSheetState.EXPANDED,
onBackClick = { params.onBackClicked() },
)
}
@Composable

View file

@ -289,6 +289,8 @@ internal class MarketsTokenDetailsModel @Inject constructor(
onScroll = {},
),
onShareClick = ::onShareClick,
isAddToPortfolioButtonVisible = false,
onAddToPortfolioClick = ::openAddToPortfolio,
),
)
@ -344,6 +346,18 @@ internal class MarketsTokenDetailsModel @Inject constructor(
}
}
}
if (isAddToPortfolioAvailable) {
addToPortfolioManager.setTokenParams(params.token)
addToPortfolioManager.state
.map { managerState ->
managerState is AddToPortfolioManager.State.Ready && managerState.isAvailableToAdd
}
.distinctUntilChanged()
.onEach { isVisible ->
state.update { it.copy(isAddToPortfolioButtonVisible = isVisible) }
}
.launchIn(modelScope)
}
addToPortfolioManager.onDismiss.receiveAsFlow()
.onEach { addToPortfolioSheetNavigation.dismiss() }
.launchIn(modelScope)

View file

@ -0,0 +1,122 @@
package com.tangem.features.feed.ui.market.detailed
import androidx.compose.animation.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.R
import com.tangem.core.ui.components.haze.hazeEffectTangem
import com.tangem.core.ui.ds.topbar.TangemTopBar
import com.tangem.core.ui.ds.topbar.TangemTopBarType
import com.tangem.core.ui.extensions.clickableSingle
import com.tangem.core.ui.res.LocalRedesignEnabled
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.generated.icons.Icons
import com.tangem.core.ui.res.generated.icons.ic_sign_plus_24
import com.tangem.features.feed.ui.LocalIsOpenedInBottomSheet
import com.tangem.features.feed.ui.market.detailed.state.MarketsTokenDetailsUM
@Composable
internal fun MarketsTokenDetailsTitle(
state: MarketsTokenDetailsUM,
backgroundColor: Color,
isBackButtonEnabled: Boolean,
onBackClick: () -> Unit,
) {
if (LocalRedesignEnabled.current) {
MarketsTokenDetailsRedesignTopBar(
isAddToPortfolioButtonVisible = state.isAddToPortfolioButtonVisible,
onAddToPortfolioClick = state.onAddToPortfolioClick,
onShareClick = state.onShareClick,
isBackButtonEnabled = isBackButtonEnabled,
onBackClick = onBackClick,
)
} else {
MarketsTokenDetailsTopBar(
onBackClick = onBackClick,
isBackButtonEnabled = isBackButtonEnabled,
shouldShowPriceSubtitle = state.shouldShowPriceSubtitle,
tokenName = state.tokenName,
tokenPrice = state.priceText,
backgroundColor = backgroundColor,
onShareClick = state.onShareClick,
)
}
}
@Composable
private fun MarketsTokenDetailsRedesignTopBar(
isAddToPortfolioButtonVisible: Boolean,
onAddToPortfolioClick: () -> Unit,
onShareClick: () -> Unit,
isBackButtonEnabled: Boolean,
onBackClick: () -> Unit,
) {
TangemTopBar(
startContent = {
TopBarHazeIconButton(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_arrow_back_28),
onClick = onBackClick,
enabled = isBackButtonEnabled,
contentPadding = TangemTheme.dimens2.x2,
)
},
endContent = {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
AnimatedVisibility(
visible = isAddToPortfolioButtonVisible,
enter = fadeIn() + scaleIn(),
exit = fadeOut() + scaleOut(),
) {
TopBarHazeIconButton(
imageVector = Icons.ic_sign_plus_24,
onClick = onAddToPortfolioClick,
enabled = isBackButtonEnabled,
contentPadding = TangemTheme.dimens2.x2_5,
)
}
TopBarHazeIconButton(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_share_new_24),
onClick = onShareClick,
enabled = isBackButtonEnabled,
contentPadding = TangemTheme.dimens2.x2_5,
)
}
},
type = if (LocalIsOpenedInBottomSheet.current) {
TangemTopBarType.BottomSheet
} else {
TangemTopBarType.Default
},
)
}
@Composable
private fun TopBarHazeIconButton(imageVector: ImageVector, onClick: () -> Unit, enabled: Boolean, contentPadding: Dp) {
Icon(
imageVector = imageVector,
contentDescription = null,
tint = TangemTheme.colors2.graphic.neutral.primary,
modifier = Modifier
.size(TangemTheme.dimens2.x11)
.clip(CircleShape)
.hazeEffectTangem { blurRadius = 8.dp }
.clickableSingle(onClick = onClick, enabled = enabled)
.padding(contentPadding),
)
}

View file

@ -51,6 +51,8 @@ internal object MarketsTokenDetailsPreview {
onScroll = {},
),
onShareClick = {},
isAddToPortfolioButtonVisible = false,
onAddToPortfolioClick = {},
priceAnnotated = stringReference("$0.00000000324"),
)
@ -147,6 +149,8 @@ internal object MarketsTokenDetailsPreview {
onScroll = {},
),
onShareClick = {},
isAddToPortfolioButtonVisible = false,
onAddToPortfolioClick = {},
priceAnnotated = stringReference("$0.00000000324"),
)
}

View file

@ -32,6 +32,8 @@ internal data class MarketsTokenDetailsUM(
val onShouldShowPriceSubtitleChange: (Boolean) -> Unit,
val relatedNews: RelatedNews,
val onShareClick: () -> Unit,
val isAddToPortfolioButtonVisible: Boolean,
val onAddToPortfolioClick: () -> Unit,
val scrollToSection: StateEvent<String> = consumedEvent(),
) {