Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-24 07:27:41 +01:00
parent 35f4f97408
commit 8922d23eec
19 changed files with 374 additions and 95 deletions

View file

@ -102,6 +102,7 @@ fun TangemTopBar(
endContent = endContent,
content = {
Column(
modifier = Modifier.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x0_5),
) {
@ -128,47 +129,54 @@ fun TangemTopBar(
* A top bar composable that displays a title and optional start and end icons.
* [Figma](https://www.figma.com/design/RU7AIgwHtGdMfy83T5UOoR/Core-Library?node-id=8435-74860&m=dev)
*
* @param modifier Modifier to be applied to the top bar.
* @param type Type of the top bar, which determines its size and padding.
* @param content Composable content to be displayed in the center of the top bar
* @param startContent Optional composable content to be displayed at the start (left) of the top bar.
* @param endContent Optional composable content to be displayed at the end (right) of the top bar.
* @param modifier Modifier to be applied to the top bar.
* @param type Type of the top bar, which determines its size and padding.
* @param content Composable content to be displayed in the center of the top bar
* @param startContent Optional composable content to be displayed at the start (left) of the top bar.
* @param endContent Optional composable content to be displayed at the end (right) of the top bar.
* @param reserveSlotSpace If true (default), slot space is always reserved even when start/end content is null,
* ensuring centered alignment of [content]. Set to false for edge-to-edge content like
* search fields, where empty slots should not consume space.
*/
@Composable
fun TangemTopBar(
modifier: Modifier = Modifier,
type: TangemTopBarType = TangemTopBarType.Default,
content: @Composable () -> Unit,
reserveSlotSpace: Boolean = true,
content: @Composable RowScope.() -> Unit,
startContent: @Composable (() -> Unit)? = null,
endContent: @Composable (() -> Unit)? = null,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = modifier
.fillMaxWidth()
.heightIn(min = type.getSize())
.padding(type.getPadding()),
) {
AnimatedContent(
targetState = startContent != null,
modifier = Modifier.size(TangemTheme.dimens2.x11),
label = "Start Content Visibility",
) { isVisible ->
if (isVisible) {
startContent?.invoke()
if (reserveSlotSpace || startContent != null) {
AnimatedContent(
targetState = startContent != null,
modifier = Modifier.size(TangemTheme.dimens2.x11),
label = "Start Content Visibility",
) { isVisible ->
if (isVisible) {
startContent?.invoke()
}
}
}
content()
AnimatedContent(
targetState = endContent != null,
modifier = Modifier.size(TangemTheme.dimens2.x11),
label = "End Content Visibility",
) { isVisible ->
if (isVisible) {
endContent?.invoke()
if (reserveSlotSpace || endContent != null) {
AnimatedContent(
targetState = endContent != null,
modifier = Modifier.size(TangemTheme.dimens2.x11),
label = "End Content Visibility",
) { isVisible ->
if (isVisible) {
endContent?.invoke()
}
}
}
}

View file

@ -78,6 +78,7 @@ internal class DefaultFeedEntryComponent @AssistedInject constructor(
screenSource = AnalyticsParam.ScreensSources.Token,
)
},
onMarketOpenClick = { onMarketOpenClick(null) },
),
),
)
@ -136,7 +137,10 @@ internal class DefaultFeedEntryComponent @AssistedInject constructor(
override fun onOpenEarnPage() {
innerRouter.push(
FeedEntryChildFactory.Child.Earn(
params = DefaultEarnComponent.Params(onBackClick = { onChildBack() }),
params = DefaultEarnComponent.Params(
onBackClick = { onChildBack() },
onMarketOpenClick = { onMarketOpenClick(null) },
),
),
)
}
@ -240,6 +244,7 @@ internal class DefaultFeedEntryComponent @AssistedInject constructor(
paginationConfig = null,
)
},
onMarketOpenClick = { clickIntents.onMarketOpenClick(null) },
),
)
FeedEntryRoute.MarketTokenList -> FeedEntryChildFactory.Child.TokenList(

View file

@ -1,9 +1,17 @@
package com.tangem.features.feed.components.earn
import androidx.compose.foundation.background
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.State
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.extensions.compose.subscribeAsState
@ -17,11 +25,15 @@ import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.core.ui.decompose.ComposableModularBottomSheetContentComponent
import com.tangem.core.ui.extensions.clickableSingle
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.LocalMainBottomSheetColor
import com.tangem.core.ui.res.LocalRedesignEnabled
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.feed.components.feed.FeedBottomSheetRoute
import com.tangem.features.feed.components.market.details.portfolio.add.AddToPortfolioPreselectedDataComponent
import com.tangem.features.feed.model.earn.EarnModel
import com.tangem.features.feed.ui.components.FeedSearchBar
import com.tangem.features.feed.ui.earn.EarnContent
import kotlinx.serialization.Serializable
@ -44,15 +56,41 @@ internal class DefaultEarnComponent(
override fun Title(bottomSheetState: State<BottomSheetState>) {
val background = LocalMainBottomSheetColor.current.value
val state by earnModel.state.collectAsStateWithLifecycle()
TangemTopAppBar(
containerColor = background,
title = stringResourceSafe(R.string.earn_title),
startButton = TopAppBarButtonUM.Icon(
iconRes = R.drawable.ic_back_24,
onClicked = state.onBackClick,
isEnabled = bottomSheetState.value == BottomSheetState.EXPANDED,
),
)
if (LocalRedesignEnabled.current) {
FeedSearchBar(
isSearchBarClickable = bottomSheetState.value == BottomSheetState.EXPANDED,
feedListSearchBar = state.feedListSearchBar,
modifier = Modifier.drawBehind { drawRect(background) },
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)
.background(
color = TangemTheme.colors2.button.backgroundSecondary,
shape = CircleShape,
)
.clickableSingle(
onClick = state.onBackClick,
enabled = bottomSheetState.value == BottomSheetState.EXPANDED,
)
.padding(TangemTheme.dimens2.x2),
)
},
)
} else {
TangemTopAppBar(
containerColor = background,
title = stringResourceSafe(R.string.earn_title),
startButton = TopAppBarButtonUM.Icon(
iconRes = R.drawable.ic_back_24,
onClicked = state.onBackClick,
isEnabled = bottomSheetState.value == BottomSheetState.EXPANDED,
),
)
}
}
@Composable
@ -94,5 +132,6 @@ internal class DefaultEarnComponent(
@Serializable
data class Params(
val onBackClick: () -> Unit,
val onMarketOpenClick: () -> Unit,
)
}

View file

@ -1,10 +1,18 @@
package com.tangem.features.feed.components.market.details
import androidx.compose.foundation.background
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.drawBehind
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.lifecycle.compose.LifecycleStartEffect
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.blockchainsdk.compatibility.getTokenIdIfL2Network
@ -12,9 +20,13 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.child
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.R
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
import com.tangem.core.ui.decompose.ComposableModularBottomSheetContentComponent
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.TokenMarketParams
import com.tangem.domain.models.currency.CryptoCurrency
@ -22,6 +34,7 @@ import com.tangem.features.feed.components.market.details.portfolio.api.MarketsP
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.components.FeedSearchBar
import com.tangem.features.feed.ui.market.detailed.MarketsTokenDetailsContent
import com.tangem.features.feed.ui.market.detailed.MarketsTokenDetailsTopBar
import kotlinx.coroutines.flow.collectLatest
@ -86,15 +99,59 @@ internal class DefaultMarketsTokenDetailsComponent(
override fun Title(bottomSheetState: State<BottomSheetState>) {
val state by model.state.collectAsStateWithLifecycle()
val background = LocalMainBottomSheetColor.current.value
MarketsTokenDetailsTopBar(
onBackClick = { params.onBackClicked() },
isBackButtonEnabled = bottomSheetState.value == BottomSheetState.EXPANDED,
shouldShowPriceSubtitle = state.shouldShowPriceSubtitle,
tokenName = state.tokenName,
tokenPrice = state.priceText,
backgroundColor = background,
onShareClick = state.onShareClick,
)
if (LocalRedesignEnabled.current) {
FeedSearchBar(
isSearchBarClickable = bottomSheetState.value == BottomSheetState.EXPANDED,
feedListSearchBar = state.feedListSearchBar,
modifier = Modifier.drawBehind { drawRect(background) },
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)
.background(
color = TangemTheme.colors2.button.backgroundSecondary,
shape = CircleShape,
)
.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)
.background(
color = TangemTheme.colors2.button.backgroundSecondary,
shape = CircleShape,
)
.clickableSingle(
onClick = state.onShareClick,
enabled = bottomSheetState.value == BottomSheetState.EXPANDED,
)
.padding(TangemTheme.dimens2.x2_5),
)
},
)
} else {
MarketsTokenDetailsTopBar(
onBackClick = { params.onBackClicked() },
isBackButtonEnabled = bottomSheetState.value == BottomSheetState.EXPANDED,
shouldShowPriceSubtitle = state.shouldShowPriceSubtitle,
tokenName = state.tokenName,
tokenPrice = state.priceText,
backgroundColor = background,
onShareClick = state.onShareClick,
)
}
}
@Composable
@ -131,6 +188,7 @@ internal class DefaultMarketsTokenDetailsComponent(
val analyticsParams: AnalyticsParams?,
val onBackClicked: () -> Unit,
val onArticleClick: (articleId: Int, preselectedArticlesId: List<Int>) -> Unit,
val onMarketOpenClick: () -> Unit,
)
@Serializable

View file

@ -22,7 +22,6 @@ 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.extensions.resourceReference
import com.tangem.core.ui.res.LocalMainBottomSheetColor
import com.tangem.core.ui.res.LocalRedesignEnabled
import com.tangem.core.ui.res.TangemTheme
@ -47,7 +46,6 @@ internal class DefaultNewsDetailsComponent(
val state by newsDetailsModel.state.collectAsStateWithLifecycle()
if (LocalRedesignEnabled.current) {
TangemTopBar(
title = resourceReference(R.string.common_news),
type = TangemTopBarType.BottomSheet,
startContent = {
Icon(

View file

@ -325,6 +325,7 @@ internal class EarnModel @Inject constructor(
onNetworkFilterClick = ::onNetworkFilterClick,
onTypeFilterClick = ::onTypeFilterClick,
onScroll = ::onMostlyUsedScrolled,
onSearchBarClicked = params.onMarketOpenClick,
),
)
}

View file

@ -1,8 +1,10 @@
package com.tangem.features.feed.model.earn.state
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.ui.extensions.TextReference
import com.tangem.features.feed.model.earn.state.transformers.EarnUMTransformer
import com.tangem.features.feed.ui.earn.state.*
import com.tangem.features.feed.ui.feed.state.FeedListSearchBar
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@ -34,6 +36,10 @@ internal class EarnStateController @Inject constructor() {
onNetworkFilterClick = {},
onTypeFilterClick = {},
onSliderScroll = {},
feedListSearchBar = FeedListSearchBar(
placeholderText = TextReference.EMPTY,
onBarClick = {},
),
)
}
}

View file

@ -1,12 +1,16 @@
package com.tangem.features.feed.model.earn.state.transformers
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.features.feed.impl.R
import com.tangem.features.feed.ui.earn.state.EarnUM
import com.tangem.features.feed.ui.feed.state.FeedListSearchBar
internal class UpdateEarnUMInitialStateTransformer(
private val onBackClick: () -> Unit,
private val onNetworkFilterClick: () -> Unit,
private val onTypeFilterClick: () -> Unit,
private val onScroll: () -> Unit,
private val onSearchBarClicked: () -> Unit,
) : EarnUMTransformer {
override fun transform(prevState: EarnUM): EarnUM {
@ -15,6 +19,10 @@ internal class UpdateEarnUMInitialStateTransformer(
onNetworkFilterClick = onNetworkFilterClick,
onTypeFilterClick = onTypeFilterClick,
onSliderScroll = onScroll,
feedListSearchBar = FeedListSearchBar(
onBarClick = onSearchBarClicked,
placeholderText = resourceReference(id = R.string.markets_search_title_placeholder),
),
)
}
}

View file

@ -232,7 +232,13 @@ internal class FeedComponentModel @Inject constructor(
return FeedListUM(
currentDate = getCurrentDate(),
feedListSearchBar = FeedListSearchBar(
placeholderText = resourceReference(R.string.markets_search_header_title),
placeholderText = resourceReference(
id = if (feedFeatureToggle.isEarnBlockEnabled) {
R.string.markets_search_title_placeholder
} else {
R.string.markets_search_header_title
},
),
onBarClick = {
analyticsEventHandler.send(FeedAnalyticsEvent.TokenSearchedClicked())
params.feedClickIntents.onMarketOpenClick(null)

View file

@ -53,6 +53,7 @@ import com.tangem.features.feed.model.market.details.converter.TokenMarketInfoCo
import com.tangem.features.feed.model.market.details.formatter.*
import com.tangem.features.feed.model.market.details.state.QuotesStateUpdater
import com.tangem.features.feed.model.market.details.state.TokenNetworksState
import com.tangem.features.feed.ui.feed.state.FeedListSearchBar
import com.tangem.features.feed.ui.market.detailed.state.ExchangesBottomSheetContent
import com.tangem.features.feed.ui.market.detailed.state.MarketsTokenDetailsUM
import com.tangem.lib.crypto.BlockchainUtils
@ -258,6 +259,10 @@ internal class MarketsTokenDetailsModel @Inject constructor(
onScroll = {},
),
onShareClick = ::onShareClick,
feedListSearchBar = FeedListSearchBar(
onBarClick = { params.onMarketOpenClick() },
placeholderText = resourceReference(id = R.string.markets_search_title_placeholder),
),
),
)

View file

@ -0,0 +1,136 @@
package com.tangem.features.feed.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
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.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.R
import com.tangem.core.ui.components.SpacerW
import com.tangem.core.ui.ds.topbar.TangemTopBar
import com.tangem.core.ui.ds.topbar.TangemTopBarType
import com.tangem.core.ui.extensions.conditional
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.LocalRedesignEnabled
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.feed.ui.feed.state.FeedListSearchBar
@Composable
internal fun FeedSearchBar(
isSearchBarClickable: Boolean,
feedListSearchBar: FeedListSearchBar,
modifier: Modifier = Modifier,
startContent: @Composable (() -> Unit)? = null,
endContent: @Composable (() -> Unit)? = null,
) {
if (LocalRedesignEnabled.current) {
FeedSearchBarV2(
isSearchBarClickable = isSearchBarClickable,
feedListSearchBar = feedListSearchBar,
modifier = modifier,
startContent = startContent,
endContent = endContent,
)
} else {
FeedSearchBarV1(
isSearchBarClickable = isSearchBarClickable,
feedListSearchBar = feedListSearchBar,
modifier = modifier,
)
}
}
@Composable
private fun FeedSearchBarV1(
isSearchBarClickable: Boolean,
feedListSearchBar: FeedListSearchBar,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier
.padding(horizontal = 16.dp)
.padding(bottom = 8.dp)
.fillMaxWidth()
.clip(RoundedCornerShape(36.dp))
.background(color = TangemTheme.colors.field.focused)
.conditional(condition = isSearchBarClickable) {
clickable(onClick = feedListSearchBar.onBarClick)
}
.padding(14.dp),
) {
Icon(
modifier = Modifier.size(TangemTheme.dimens.size20),
imageVector = ImageVector.vectorResource(id = R.drawable.ic_search_24),
tint = TangemTheme.colors.icon.informative,
contentDescription = null,
)
SpacerW(14.dp)
Text(
text = feedListSearchBar.placeholderText.resolveReference(),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
)
}
}
@Composable
private fun FeedSearchBarV2(
isSearchBarClickable: Boolean,
feedListSearchBar: FeedListSearchBar,
modifier: Modifier = Modifier,
startContent: @Composable (() -> Unit)? = null,
endContent: @Composable (() -> Unit)? = null,
) {
TangemTopBar(
modifier = modifier,
startContent = startContent,
endContent = endContent,
type = TangemTopBarType.BottomSheet,
reserveSlotSpace = false,
content = {
Row(
modifier = Modifier
.weight(1f)
.padding(
start = if (startContent != null) TangemTheme.dimens2.x3 else 0.dp,
end = if (endContent != null) TangemTheme.dimens2.x3 else 0.dp,
)
.clip(CircleShape)
.background(color = TangemTheme.colors2.field.backgroundDefault)
.conditional(condition = isSearchBarClickable) {
clickable(onClick = feedListSearchBar.onBarClick)
}
.padding(TangemTheme.dimens2.x3),
horizontalArrangement = Arrangement.Center,
) {
Icon(
modifier = Modifier.size(TangemTheme.dimens2.x5),
imageVector = ImageVector.vectorResource(id = R.drawable.ic_search_default_24),
tint = TangemTheme.colors2.markers.iconGray,
contentDescription = null,
)
SpacerW(TangemTheme.dimens2.x1)
Text(
text = feedListSearchBar.placeholderText.resolveReference(),
style = TangemTheme.typography2.bodySemibold16,
color = TangemTheme.colors2.text.neutral.tertiary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
},
)
}

View file

@ -22,6 +22,7 @@ import com.tangem.core.ui.components.*
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.list.InfiniteListHandler
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.conditional
import com.tangem.core.ui.extensions.conditionalCompose
import com.tangem.core.ui.extensions.stringReference
@ -29,6 +30,7 @@ import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.*
import com.tangem.features.feed.ui.earn.components.*
import com.tangem.features.feed.ui.earn.state.*
import com.tangem.features.feed.ui.feed.state.FeedListSearchBar
import kotlinx.collections.immutable.persistentListOf
private const val EARN_LOAD_MORE_BUFFER = 3
@ -655,6 +657,10 @@ private fun previewEarnUM(
onNetworkFilterClick = {},
onTypeFilterClick = {},
onSliderScroll = {},
feedListSearchBar = FeedListSearchBar(
placeholderText = TextReference.Str("Search tokens & news"),
onBarClick = {},
),
)
private const val PLACEHOLDER_ITEMS_COUNT = 8

View file

@ -1,6 +1,7 @@
package com.tangem.features.feed.ui.earn.state
import androidx.compose.runtime.Immutable
import com.tangem.features.feed.ui.feed.state.FeedListSearchBar
@Immutable
internal data class EarnUM(
@ -11,4 +12,5 @@ internal data class EarnUM(
val onNetworkFilterClick: () -> Unit,
val onTypeFilterClick: () -> Unit,
val onSliderScroll: () -> Unit,
val feedListSearchBar: FeedListSearchBar,
)

View file

@ -4,34 +4,22 @@ import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.R
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.SpacerW
import com.tangem.core.ui.extensions.conditional
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.res.LocalMainBottomSheetColor
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.BaseSearchBarTestTags.SEARCH_BAR
import com.tangem.features.feed.model.market.list.state.SortByTypeUM
import com.tangem.features.feed.ui.components.FeedSearchBar
import com.tangem.features.feed.ui.feed.components.*
import com.tangem.features.feed.ui.feed.preview.FeedListPreviewDataProvider.createFeedPreviewState
import com.tangem.features.feed.ui.feed.state.FeedListSearchBar
@ -50,8 +38,6 @@ internal fun FeedListHeader(
feedListSearchBar = feedListSearchBar,
modifier = modifier
.drawBehind { drawRect(background) }
.padding(horizontal = 16.dp)
.padding(bottom = 8.dp)
.testTag(SEARCH_BAR),
)
}
@ -95,39 +81,6 @@ internal fun FeedList(
}
}
@Composable
private fun FeedSearchBar(
isSearchBarClickable: Boolean,
feedListSearchBar: FeedListSearchBar,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier
.fillMaxWidth()
.clip(RoundedCornerShape(36.dp))
.background(color = TangemTheme.colors.field.focused)
.conditional(condition = isSearchBarClickable) {
clickable(onClick = feedListSearchBar.onBarClick)
}
.padding(14.dp),
) {
Icon(
modifier = Modifier.size(TangemTheme.dimens.size20),
imageVector = ImageVector.vectorResource(id = R.drawable.ic_search_24),
tint = TangemTheme.colors.icon.informative,
contentDescription = null,
)
SpacerW(14.dp)
Text(
text = feedListSearchBar.placeholderText.resolveReference(),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
)
}
}
@Composable
private fun FeedListContent(
state: FeedListUM,

View file

@ -14,6 +14,7 @@ import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.R
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.SpacerW
import com.tangem.core.ui.components.buttons.SecondarySmallButton
import com.tangem.core.ui.components.buttons.SmallButtonConfig
@ -23,13 +24,16 @@ import com.tangem.core.ui.res.LocalRedesignEnabled
import com.tangem.core.ui.res.TangemTheme
@Composable
internal fun Header(
internal fun ColumnScope.Header(
onSeeAllClick: () -> Unit,
isLoading: Boolean,
shouldShowSeeAll: Boolean,
title: @Composable () -> Unit,
) {
val isRedesignEnabled = LocalRedesignEnabled.current
if (isRedesignEnabled) {
SpacerH(20.dp)
}
AnimatedContent(isLoading) { animatedState ->
Row(
modifier = Modifier

View file

@ -9,10 +9,20 @@ import androidx.compose.ui.unit.dp
import com.tangem.core.ui.R
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.LocalRedesignEnabled
import com.tangem.core.ui.res.TangemTheme
@Composable
internal fun DateBlock(currentDate: String) {
if (LocalRedesignEnabled.current) {
DateBlockV2(currentDate)
} else {
DateBlockV1(currentDate)
}
}
@Composable
private fun DateBlockV1(currentDate: String) {
SpacerH(20.dp)
Text(
modifier = Modifier
@ -30,4 +40,26 @@ internal fun DateBlock(currentDate: String) {
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.tertiary,
)
}
@Composable
private fun DateBlockV2(currentDate: String) {
SpacerH(TangemTheme.dimens2.x1)
Text(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp),
text = stringResourceSafe(R.string.feed_market_and_news),
style = TangemTheme.typography2.headingRegular28,
color = TangemTheme.colors2.text.neutral.primary,
)
Text(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp),
text = currentDate,
style = TangemTheme.typography2.headingRegular28,
color = TangemTheme.colors2.text.neutral.tertiary,
)
SpacerH(TangemTheme.dimens2.x6)
}

View file

@ -71,7 +71,7 @@ internal fun MarketBlock(marketChart: MarketChartUM?, feedListCallbacks: FeedLis
isLoading = currentChart is MarketChartUM.Loading,
)
SpacerH(12.dp)
SpacerH(if (isRedesignEnabled) 20.dp else 12.dp)
Charts(
onItemClick = feedListCallbacks.onMarketItemClick,
@ -89,7 +89,7 @@ internal fun MarketBlock(marketChart: MarketChartUM?, feedListCallbacks: FeedLis
@Suppress("LongMethod")
@Composable
internal fun MarketPulseBlock(marketChartConfig: MarketChartConfig, feedListCallbacks: FeedListCallbacks) {
internal fun ColumnScope.MarketPulseBlock(marketChartConfig: MarketChartConfig, feedListCallbacks: FeedListCallbacks) {
val isRedesignEnabled = LocalRedesignEnabled.current
val onSeeAllClick by rememberUpdatedState {
feedListCallbacks.onMarketOpenClick(marketChartConfig.currentSortByType)

View file

@ -5,8 +5,10 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.event.consumedEvent
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.markets.PriceChangeInterval
import com.tangem.features.feed.ui.feed.state.FeedListSearchBar
import com.tangem.features.feed.ui.market.detailed.state.*
import kotlinx.collections.immutable.persistentListOf
@ -50,6 +52,10 @@ internal object MarketsTokenDetailsPreview {
onScroll = {},
),
onShareClick = {},
feedListSearchBar = FeedListSearchBar(
placeholderText = TextReference.Str("Search tokens & news"),
onBarClick = {},
),
)
val contentState = MarketsTokenDetailsUM(
@ -144,5 +150,9 @@ internal object MarketsTokenDetailsPreview {
onScroll = {},
),
onShareClick = {},
feedListSearchBar = FeedListSearchBar(
placeholderText = TextReference.Str("Search tokens & news"),
onBarClick = {},
),
)
}

View file

@ -8,6 +8,7 @@ import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.event.StateEvent
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.markets.PriceChangeInterval
import com.tangem.features.feed.ui.feed.state.FeedListSearchBar
import kotlinx.collections.immutable.ImmutableList
import java.math.BigDecimal
@ -29,6 +30,7 @@ internal data class MarketsTokenDetailsUM(
val onShouldShowPriceSubtitleChange: (Boolean) -> Unit,
val relatedNews: RelatedNews,
val onShareClick: () -> Unit,
val feedListSearchBar: FeedListSearchBar,
) {
data class ChartState(