From 8922d23eec0348c0ed974a18111360324f93a80f Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 24 Mar 2026 07:27:41 +0100 Subject: [PATCH] Updated on 2026-08-14 --- .../tangem/core/ui/ds/topbar/TangemTopBar.kt | 50 ++++--- .../components/DefaultFeedEntryComponent.kt | 7 +- .../components/earn/DefaultEarnComponent.kt | 57 ++++++-- .../DefaultMarketsTokenDetailsComponent.kt | 76 ++++++++-- .../details/DefaultNewsDetailsComponent.kt | 2 - .../features/feed/model/earn/EarnModel.kt | 1 + .../model/earn/state/EarnStateController.kt | 6 + .../UpdateEarnUMInitialStateTransformer.kt | 8 ++ .../feed/model/feed/FeedComponentModel.kt | 8 +- .../details/MarketsTokenDetailsModel.kt | 5 + .../feed/ui/components/FeedSearchBar.kt | 136 ++++++++++++++++++ .../features/feed/ui/earn/EarnContent.kt | 6 + .../features/feed/ui/earn/state/EarnUM.kt | 2 + .../tangem/features/feed/ui/feed/FeedList.kt | 51 +------ .../feed/ui/feed/components/BlockHeader.kt | 6 +- .../feed/ui/feed/components/DateBlock.kt | 32 +++++ .../feed/ui/feed/components/MarketsBlock.kt | 4 +- .../preview/MarketsTokenDetailsPreview.kt | 10 ++ .../detailed/state/MarketsTokenDetailsUM.kt | 2 + 19 files changed, 374 insertions(+), 95 deletions(-) create mode 100644 features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/components/FeedSearchBar.kt diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/topbar/TangemTopBar.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/topbar/TangemTopBar.kt index 9b687679f5..c05ffb7daa 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/topbar/TangemTopBar.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/topbar/TangemTopBar.kt @@ -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() + } } } } diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/DefaultFeedEntryComponent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/DefaultFeedEntryComponent.kt index 9b8a539048..9538938a8c 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/DefaultFeedEntryComponent.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/DefaultFeedEntryComponent.kt @@ -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( diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/earn/DefaultEarnComponent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/earn/DefaultEarnComponent.kt index a0b7dcb1e8..e5915d7103 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/earn/DefaultEarnComponent.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/earn/DefaultEarnComponent.kt @@ -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) { 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, ) } \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/DefaultMarketsTokenDetailsComponent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/DefaultMarketsTokenDetailsComponent.kt index d43ace2233..93292cc527 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/DefaultMarketsTokenDetailsComponent.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/DefaultMarketsTokenDetailsComponent.kt @@ -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) { 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) -> Unit, + val onMarketOpenClick: () -> Unit, ) @Serializable diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/news/details/DefaultNewsDetailsComponent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/news/details/DefaultNewsDetailsComponent.kt index 1d31f11fe9..7b0b6465db 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/news/details/DefaultNewsDetailsComponent.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/news/details/DefaultNewsDetailsComponent.kt @@ -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( diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/earn/EarnModel.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/earn/EarnModel.kt index 8622c63f4b..71a2894d6b 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/earn/EarnModel.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/earn/EarnModel.kt @@ -325,6 +325,7 @@ internal class EarnModel @Inject constructor( onNetworkFilterClick = ::onNetworkFilterClick, onTypeFilterClick = ::onTypeFilterClick, onScroll = ::onMostlyUsedScrolled, + onSearchBarClicked = params.onMarketOpenClick, ), ) } diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/earn/state/EarnStateController.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/earn/state/EarnStateController.kt index f074cbdc66..34a71d62a2 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/earn/state/EarnStateController.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/earn/state/EarnStateController.kt @@ -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 = {}, + ), ) } } \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/earn/state/transformers/UpdateEarnUMInitialStateTransformer.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/earn/state/transformers/UpdateEarnUMInitialStateTransformer.kt index d46ac02956..8991f72503 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/earn/state/transformers/UpdateEarnUMInitialStateTransformer.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/earn/state/transformers/UpdateEarnUMInitialStateTransformer.kt @@ -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), + ), ) } } \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/FeedComponentModel.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/FeedComponentModel.kt index 9fb42bb041..3252a220e0 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/FeedComponentModel.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/FeedComponentModel.kt @@ -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) diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/details/MarketsTokenDetailsModel.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/details/MarketsTokenDetailsModel.kt index e155e80515..f362ecfdab 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/details/MarketsTokenDetailsModel.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/details/MarketsTokenDetailsModel.kt @@ -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), + ), ), ) diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/components/FeedSearchBar.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/components/FeedSearchBar.kt new file mode 100644 index 0000000000..34aa87a805 --- /dev/null +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/components/FeedSearchBar.kt @@ -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, + ) + } + }, + ) +} \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/earn/EarnContent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/earn/EarnContent.kt index 5f9756b351..86ca75fa5b 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/earn/EarnContent.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/earn/EarnContent.kt @@ -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 diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/earn/state/EarnUM.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/earn/state/EarnUM.kt index 974b5a71b2..fd8611ff2b 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/earn/state/EarnUM.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/earn/state/EarnUM.kt @@ -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, ) \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/FeedList.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/FeedList.kt index e8267f9b85..26f97c3c93 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/FeedList.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/FeedList.kt @@ -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, diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/components/BlockHeader.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/components/BlockHeader.kt index a1183e5467..32a6229d2e 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/components/BlockHeader.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/components/BlockHeader.kt @@ -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 diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/components/DateBlock.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/components/DateBlock.kt index de6399ee68..b38b335ae1 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/components/DateBlock.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/components/DateBlock.kt @@ -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) } \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/components/MarketsBlock.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/components/MarketsBlock.kt index b24607dc57..832e2e4ea0 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/components/MarketsBlock.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/components/MarketsBlock.kt @@ -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) diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/preview/MarketsTokenDetailsPreview.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/preview/MarketsTokenDetailsPreview.kt index ed7261d399..65ce49e61e 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/preview/MarketsTokenDetailsPreview.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/preview/MarketsTokenDetailsPreview.kt @@ -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 = {}, + ), ) } \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/state/MarketsTokenDetailsUM.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/state/MarketsTokenDetailsUM.kt index 5f21370028..c6e0bde66a 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/state/MarketsTokenDetailsUM.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/state/MarketsTokenDetailsUM.kt @@ -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(