From 6b9aacb1ea6a5f590da7c6c1a24f0f55969f5617 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 19 Dec 2025 14:08:10 +0100 Subject: [PATCH] Updated on 2026-08-14 --- .../com/tangem/common/ui/news/ArticleCard.kt | 38 ++++++++ .../core/ui/components/fields/SearchBar.kt | 20 ++-- .../components/fields/entity/SearchBarUM.kt | 1 + .../res/drawable/ic_show_more_news_48.xml | 18 ++++ .../components/DefaultFeedEntryComponent.kt | 6 +- .../components/feed/DefaultFeedComponent.kt | 2 +- .../list/DefaultMarketsTokenListComponent.kt | 6 +- .../feed/model/feed/FeedComponentModel.kt | 19 +--- .../feed/model/feed/FeedModelClickIntents.kt | 2 +- .../model/market/list/MarketsListModel.kt | 1 + .../statemanager/MarketsListUMStateManager.kt | 47 +++++++--- .../tangem/features/feed/ui/feed/FeedList.kt | 54 ++++++++--- .../preview/FeedListPreviewDataProvider.kt | 8 +- .../features/feed/ui/feed/state/FeedListUM.kt | 9 +- .../ui/feed/state/SearchBarStateFactory.kt | 24 ----- .../feed/ui/market/list/MarketsList.kt | 94 ++++++++++++------- .../ui/market/list/state/MarketsListUM.kt | 16 +++- .../list/state/SortByBottomSheetContentUM.kt | 2 +- 18 files changed, 235 insertions(+), 132 deletions(-) create mode 100644 core/ui/src/main/res/drawable/ic_show_more_news_48.xml delete mode 100644 features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/state/SearchBarStateFactory.kt diff --git a/common/ui/src/main/java/com/tangem/common/ui/news/ArticleCard.kt b/common/ui/src/main/java/com/tangem/common/ui/news/ArticleCard.kt index 91e26bb895..3b3f257ab5 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/news/ArticleCard.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/news/ArticleCard.kt @@ -1,6 +1,7 @@ package com.tangem.common.ui.news import android.content.res.Configuration +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState @@ -12,6 +13,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -100,6 +103,41 @@ private fun TrendingArticle(articleConfigUM: ArticleConfigUM) { } } +@Composable +fun ShowMoreArticlesCard(modifier: Modifier = Modifier, onClick: () -> Unit) { + BlockCard( + modifier = modifier, + onClick = onClick, + colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action), + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .fillMaxSize() + .padding(vertical = 31.dp, horizontal = 12.dp), + ) { + Image( + imageVector = ImageVector.vectorResource(R.drawable.ic_show_more_news_48), + contentDescription = stringResourceSafe(R.string.common_show_more), + ) + + SpacerH(16.dp) + + Text( + text = stringResourceSafe(R.string.news_all_news), + style = TangemTheme.typography.subtitle1, + color = TangemTheme.colors.text.primary1, + ) + + Text( + text = stringResourceSafe(R.string.news_stay_in_the_loop), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + ) + } + } +} + @Composable private fun DefaultArticle(articleConfigUM: ArticleConfigUM) { Column(modifier = Modifier.padding(12.dp)) { diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/SearchBar.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/SearchBar.kt index c0390dfda0..17ddadc6ff 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/fields/SearchBar.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/SearchBar.kt @@ -10,9 +10,8 @@ import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.* -import androidx.compose.runtime.Composable -import androidx.compose.runtime.Stable -import androidx.compose.runtime.remember +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusManager import androidx.compose.ui.focus.FocusRequester @@ -51,16 +50,22 @@ fun SearchBar( val keyboardController = LocalSoftwareKeyboardController.current val focusManager = LocalFocusManager.current val interactionSource = remember { MutableInteractionSource() } + var isInitialComposition by rememberSaveable { mutableStateOf(true) } + LaunchedEffect(Unit) { + isInitialComposition = false + } BasicTextField( modifier = modifier .fillMaxWidth() .heightIn(min = TangemTheme.dimens.size48) .onFocusChanged { focusState -> - if (focusState.isFocused) { - state.onActiveChange(true) - } else { - state.onActiveChange(false) + if (!isInitialComposition) { + if (focusState.isFocused) { + state.onActiveChange(true) + } else { + state.onActiveChange(false) + } } } .focusRequester(focusRequester) @@ -163,6 +168,7 @@ private fun ClearButton( focusManager.clearFocus() keyboardController?.hide() state.onActiveChange(false) + state.onClearClick() }, ) { Icon( diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/entity/SearchBarUM.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/entity/SearchBarUM.kt index d2ac8fcc8b..158c768fe8 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/fields/entity/SearchBarUM.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/entity/SearchBarUM.kt @@ -8,4 +8,5 @@ data class SearchBarUM( val onQueryChange: (String) -> Unit, val isActive: Boolean, val onActiveChange: (Boolean) -> Unit, + val onClearClick: () -> Unit = {}, ) \ No newline at end of file diff --git a/core/ui/src/main/res/drawable/ic_show_more_news_48.xml b/core/ui/src/main/res/drawable/ic_show_more_news_48.xml new file mode 100644 index 0000000000..fe0417c568 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_show_more_news_48.xml @@ -0,0 +1,18 @@ + + + + + + + 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 0519eeeaf2..c3f6755841 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 @@ -60,14 +60,14 @@ internal class DefaultFeedEntryComponent @AssistedInject constructor( ) } - override fun onMarketOpenClick(sortBy: SortByTypeUM) { + override fun onMarketOpenClick(sortBy: SortByTypeUM?) { innerRouter.push( route = FeedEntryChildFactory.Child.TokenList( params = DefaultMarketsTokenListComponent.Params( onBackClicked = { onChildBack() }, onTokenClick = { token, currency -> onMarketItemClick(token, currency) }, - preselectedSortType = sortBy, - shouldAlwaysShowSearchBar = sortBy == SortByTypeUM.Rating, + preselectedSortType = sortBy ?: SortByTypeUM.Rating, + shouldAlwaysShowSearchBar = sortBy == null, ), ), ) diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/feed/DefaultFeedComponent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/feed/DefaultFeedComponent.kt index 0b6ca24074..9257a465b1 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/feed/DefaultFeedComponent.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/feed/DefaultFeedComponent.kt @@ -23,7 +23,7 @@ internal class DefaultFeedComponent( @Composable override fun Title() { val state by feedComponentModel.state.collectAsStateWithLifecycle() - FeedListHeader(state.searchBar) + FeedListHeader(state.feedListSearchBar) } @Composable diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/list/DefaultMarketsTokenListComponent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/list/DefaultMarketsTokenListComponent.kt index a2f2670051..75c9ca0587 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/list/DefaultMarketsTokenListComponent.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/list/DefaultMarketsTokenListComponent.kt @@ -26,7 +26,11 @@ internal class DefaultMarketsTokenListComponent( @Composable override fun Title() { val state by model.state.collectAsStateWithLifecycle() - TopBarWithSearch(state.searchBar) + TopBarWithSearch( + onBackClick = params.onBackClicked, + onSearchClick = state.onSearchClicked, + marketsSearchBar = state.marketsSearchBar, + ) } @Composable 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 9c7a9c96b1..e61e95a217 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 @@ -5,7 +5,6 @@ import arrow.core.getOrElse import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer -import com.tangem.core.ui.components.fields.entity.SearchBarUM import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.utils.DateTimeFormatters import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase @@ -31,7 +30,6 @@ import kotlinx.coroutines.launch import org.joda.time.DateTime import org.joda.time.DateTimeZone import javax.inject.Inject -import kotlin.collections.all @Stable @ModelScoped @@ -66,13 +64,6 @@ internal class FeedComponentModel @Inject constructor( internal val state: StateFlow field = MutableStateFlow(initialState()) - private val searchBarStateFactory by lazy(LazyThreadSafetyMode.NONE) { - SearchBarStateFactory( - currentStateProvider = Provider { state.value }, - onStateUpdate = { newState -> state.update { newState } }, - ) - } - private val trendingNewsStateFactory by lazy(LazyThreadSafetyMode.NONE) { TrendingNewsStateFactory( currentStateProvider = Provider { state.value }, @@ -137,14 +128,9 @@ internal class FeedComponentModel @Inject constructor( private fun initialState(): FeedListUM { return FeedListUM( currentDate = getCurrentDate(), - searchBar = SearchBarUM( + feedListSearchBar = FeedListSearchBar( placeholderText = resourceReference(R.string.markets_search_header_title), - query = "", - onQueryChange = {}, - isActive = false, - onActiveChange = { - if (it) params.feedClickIntents.onMarketOpenClick(SortByTypeUM.Rating) - }, + onBarClick = { params.feedClickIntents.onMarketOpenClick(null) }, ), feedListCallbacks = FeedListCallbacks( onSearchClick = {}, @@ -307,7 +293,6 @@ internal class FeedComponentModel @Inject constructor( private fun updateCallbacks() { state.update { feedListUM -> feedListUM.copy( - searchBar = state.value.searchBar.copy(onQueryChange = searchBarStateFactory::onSearchQueryChange), feedListCallbacks = feedListUM.feedListCallbacks.copy( onSortTypeClick = ::onSortTypeClick, onMarketItemClick = { item -> diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/FeedModelClickIntents.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/FeedModelClickIntents.kt index 4b4c342fe0..97621a1ef9 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/FeedModelClickIntents.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/FeedModelClickIntents.kt @@ -9,7 +9,7 @@ import com.tangem.features.feed.ui.market.list.state.SortByTypeUM */ internal interface FeedModelClickIntents { fun onMarketItemClick(token: TokenMarketParams, appCurrency: AppCurrency) - fun onMarketOpenClick(sortBy: SortByTypeUM) + fun onMarketOpenClick(sortBy: SortByTypeUM?) fun onArticleClick(articleId: Int) fun onOpenAllNews() } \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/MarketsListModel.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/MarketsListModel.kt index ca976fa47b..f7ae1d61d4 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/MarketsListModel.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/MarketsListModel.kt @@ -73,6 +73,7 @@ internal class MarketsListModel @Inject constructor( onShowTokensUnder100kClicked = { analyticsEventHandler.send(MarketsListAnalyticsEvent.ShowTokens()) }, shouldAlwaysShowSearchBar = Provider { params.shouldAlwaysShowSearchBar }, preselectedSortType = Provider { params.preselectedSortType }, + onBackClick = params.onBackClicked, ) } diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/statemanager/MarketsListUMStateManager.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/statemanager/MarketsListUMStateManager.kt index 5ecb6df31a..2e269facb3 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/statemanager/MarketsListUMStateManager.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/statemanager/MarketsListUMStateManager.kt @@ -29,6 +29,7 @@ internal class MarketsListUMStateManager( private val onRetryButtonClicked: () -> Unit, private val onTokenClick: (MarketsListItemUM) -> Unit, private val onShowTokensUnder100kClicked: () -> Unit, + private val onBackClick: () -> Unit, ) { val state = MutableStateFlow(state()) @@ -38,19 +39,17 @@ internal class MarketsListUMStateManager( set(value) = state.update { it.copy(sortByBottomSheet = it.sortByBottomSheet.copy(isShown = value)) } var searchQuery - get() = state.value.searchBar.query + get() = state.value.marketsSearchBar.searchBarUM.query private set(value) = state.update { marketsListUM -> marketsListUM.copy( - searchBar = marketsListUM.searchBar.copy( - query = value, - isActive = value.isNotEmpty(), + marketsSearchBar = marketsListUM.marketsSearchBar.copy( + searchBarUM = marketsListUM.marketsSearchBar.searchBarUM.copy(query = value), ), ) } - var isInSearchState - get() = state.value.searchBar.isActive - private set(value) = state.update { it.copy(searchBar = it.searchBar.copy(isActive = value)) } + val isInSearchState + get() = state.value.marketsSearchBar.searchBarUM.isActive var selectedSortByType get() = state.value.selectedSortBy @@ -91,7 +90,7 @@ internal class MarketsListUMStateManager( } val isInSearchStateFlow = state.map { it.isInSearchMode }.distinctUntilChanged() - val searchQueryFlow = state.map { it.searchBar.query }.distinctUntilChanged() + val searchQueryFlow = state.map { it.marketsSearchBar.searchBarUM.query }.distinctUntilChanged() fun onUiItemsChanged( isInErrorState: Boolean, @@ -214,12 +213,20 @@ internal class MarketsListUMStateManager( private fun state(): MarketsListUM = MarketsListUM( list = ListUM.Loading, - searchBar = SearchBarUM( - placeholderText = resourceReference(R.string.markets_search_header_title), - query = "", - onQueryChange = { searchQuery = it }, - isActive = false, - onActiveChange = { }, + marketsSearchBar = MarketsSearchBar( + searchBarUM = SearchBarUM( + placeholderText = resourceReference(R.string.markets_search_header_title), + query = "", + onQueryChange = { searchQuery = it }, + isActive = false, + onActiveChange = ::changeSearchBarIsActive, + onClearClick = { + if (shouldAlwaysShowSearchBar()) { + onBackClick() + } + }, + ), + shouldAlwaysShowSearchBar = shouldAlwaysShowSearchBar(), ), selectedSortBy = preselectedSortType(), selectedInterval = MarketsListUM.TrendInterval.H24, @@ -234,7 +241,7 @@ internal class MarketsListUMStateManager( ), ), marketsNotificationUM = null, - shouldAlwaysShowSearchBar = shouldAlwaysShowSearchBar(), + onSearchClicked = { changeSearchBarIsActive(true) }, ) private fun onBottomSheetOptionClicked(sortByTypeUM: SortByTypeUM) { @@ -264,4 +271,14 @@ internal class MarketsListUMStateManager( ) } } + + private fun changeSearchBarIsActive(isActive: Boolean) { + state.update { marketsListUM -> + marketsListUM.copy( + marketsSearchBar = marketsListUM.marketsSearchBar.copy( + searchBarUM = marketsListUM.marketsSearchBar.searchBarUM.copy(isActive = isActive), + ), + ) + } + } } \ 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 49bd19e7af..4cc64dcbba 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 @@ -8,6 +8,7 @@ import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.material3.ripple import androidx.compose.runtime.Composable @@ -33,6 +34,7 @@ import com.tangem.common.ui.markets.MarketsListItemPlaceholder import com.tangem.common.ui.markets.models.MarketsListItemUM import com.tangem.common.ui.news.ArticleCard import com.tangem.common.ui.news.ArticleConfigUM +import com.tangem.common.ui.news.ShowMoreArticlesCard import com.tangem.core.ui.R import com.tangem.core.ui.components.SpacerH import com.tangem.core.ui.components.SpacerW @@ -41,9 +43,6 @@ import com.tangem.core.ui.components.block.BlockCard import com.tangem.core.ui.components.block.TangemBlockCardColors import com.tangem.core.ui.components.buttons.SecondarySmallButton import com.tangem.core.ui.components.buttons.SmallButtonConfig -import com.tangem.core.ui.components.fields.SearchBar -import com.tangem.core.ui.components.fields.TangemSearchBarDefaults -import com.tangem.core.ui.components.fields.entity.SearchBarUM import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.stringResourceSafe @@ -55,25 +54,20 @@ import com.tangem.features.feed.ui.feed.state.* import com.tangem.features.feed.ui.market.list.state.SortByTypeUM @Composable -internal fun FeedListHeader(searchBarUM: SearchBarUM, modifier: Modifier = Modifier) { +internal fun FeedListHeader(feedListSearchBar: FeedListSearchBar, modifier: Modifier = Modifier) { val background = LocalMainBottomSheetColor.current.value - SearchBar( + FeedSearchBar( + feedListSearchBar = feedListSearchBar, modifier = modifier .drawBehind { drawRect(background) } .padding(horizontal = 16.dp) .padding(bottom = 12.dp), - state = searchBarUM, - colors = TangemSearchBarDefaults.defaultTextFieldColors.copy( - focusedContainerColor = TangemTheme.colors.field.focused, - unfocusedContainerColor = TangemTheme.colors.field.focused, - ), ) } @Composable internal fun FeedList(state: FeedListUM, modifier: Modifier = Modifier) { val background = LocalMainBottomSheetColor.current.value - AnimatedContent( modifier = modifier, targetState = state.globalState, @@ -103,6 +97,33 @@ internal fun FeedList(state: FeedListUM, modifier: Modifier = Modifier) { } } +@Composable +private fun FeedSearchBar(feedListSearchBar: FeedListSearchBar, modifier: Modifier = Modifier) { + Row( + modifier = modifier + .fillMaxWidth() + .clip(RoundedCornerShape(36.dp)) + .background(color = TangemTheme.colors.field.focused) + .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 FeeListContent(state: FeedListUM, modifier: Modifier = Modifier) { val background = LocalMainBottomSheetColor.current.value @@ -308,7 +329,7 @@ private fun NewsContentBlock( append(stringResourceSafe(R.string.feed_tangem_ai)) } }, - style = TangemTheme.typography.h3, + style = TangemTheme.typography.subtitle1, ) } }, @@ -345,10 +366,17 @@ private fun NewsContentBlock( onArticleClick = { feedListCallbacks.onArticleClick(article.id) }, modifier = Modifier .height(164.dp) - .widthIn(max = 216.dp), + .width(216.dp), colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action), ) } + + item { + ShowMoreArticlesCard( + modifier = Modifier.size(width = 216.dp, height = 164.dp), + onClick = feedListCallbacks.onOpenAllNews, + ) + } } SpacerH(32.dp) } diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/preview/FeedListPreviewDataProvider.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/preview/FeedListPreviewDataProvider.kt index d0d833fcd1..50b22f6f46 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/preview/FeedListPreviewDataProvider.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/preview/FeedListPreviewDataProvider.kt @@ -3,7 +3,6 @@ package com.tangem.features.feed.ui.feed.preview import com.tangem.common.ui.charts.state.MarketChartRawData import com.tangem.common.ui.markets.models.MarketsListItemUM import com.tangem.common.ui.news.ArticleConfigUM -import com.tangem.core.ui.components.fields.entity.SearchBarUM import com.tangem.core.ui.components.label.entity.LabelLeadingContentUM import com.tangem.core.ui.components.label.entity.LabelUM import com.tangem.core.ui.components.marketprice.PriceChangeType @@ -22,12 +21,9 @@ internal object FeedListPreviewDataProvider { val marketItems = createSampleMarketItems() return FeedListUM( currentDate = "20 November", - searchBar = SearchBarUM( + feedListSearchBar = FeedListSearchBar( placeholderText = TextReference.Str("Search tokens & news"), - query = "", - onQueryChange = {}, - isActive = false, - onActiveChange = {}, + onBarClick = {}, ), feedListCallbacks = FeedListCallbacks( onSearchClick = {}, diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/state/FeedListUM.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/state/FeedListUM.kt index 269fcb8d30..3c05e90f30 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/state/FeedListUM.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/state/FeedListUM.kt @@ -3,7 +3,7 @@ package com.tangem.features.feed.ui.feed.state import androidx.compose.runtime.Immutable import com.tangem.common.ui.markets.models.MarketsListItemUM import com.tangem.common.ui.news.ArticleConfigUM -import com.tangem.core.ui.components.fields.entity.SearchBarUM +import com.tangem.core.ui.extensions.TextReference import com.tangem.features.feed.ui.market.list.state.SortByTypeUM import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableMap @@ -11,7 +11,7 @@ import kotlinx.collections.immutable.toPersistentList internal data class FeedListUM( val currentDate: String, - val searchBar: SearchBarUM, + val feedListSearchBar: FeedListSearchBar, val feedListCallbacks: FeedListCallbacks, val news: NewsUM, val trendingArticle: ArticleConfigUM?, @@ -28,6 +28,11 @@ internal data class FeedListCallbacks( val onSortTypeClick: (SortByTypeUM) -> Unit, ) +internal data class FeedListSearchBar( + val onBarClick: () -> Unit, + val placeholderText: TextReference, +) + @Immutable internal sealed interface NewsUM { data object Loading : NewsUM diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/state/SearchBarStateFactory.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/state/SearchBarStateFactory.kt deleted file mode 100644 index bd33df97cc..0000000000 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/state/SearchBarStateFactory.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.tangem.features.feed.ui.feed.state - -import com.tangem.utils.Provider - -internal class SearchBarStateFactory( - private val currentStateProvider: Provider, - private val onStateUpdate: (FeedListUM) -> Unit, -) { - - val searchQuery: String - get() = currentStateProvider().searchBar.query - - fun onSearchQueryChange(query: String) { - val currentState = currentStateProvider() - onStateUpdate( - currentState.copy( - searchBar = currentState.searchBar.copy( - query = query, - isActive = query.isNotEmpty(), - ), - ), - ) - } -} \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/list/MarketsList.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/list/MarketsList.kt index 1c86bc2b22..b4024d9bce 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/list/MarketsList.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/list/MarketsList.kt @@ -2,6 +2,7 @@ package com.tangem.features.feed.ui.market.list import android.content.res.Configuration import androidx.activity.compose.BackHandler +import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.rememberLazyListState @@ -10,6 +11,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.geometry.Offset import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalSoftwareKeyboardController @@ -19,6 +21,7 @@ import com.tangem.common.ui.markets.preview.MarketChartListItemPreviewDataProvid import com.tangem.core.ui.components.Keyboard import com.tangem.core.ui.components.SpacerH12 import com.tangem.core.ui.components.SpacerH8 +import com.tangem.core.ui.components.appbar.AppBarWithBackButtonAndIcon import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.buttons.SecondarySmallButton import com.tangem.core.ui.components.buttons.SmallButtonConfig @@ -41,23 +44,47 @@ import com.tangem.features.feed.ui.market.list.components.YieldSupplyInMarketsPr import com.tangem.features.feed.ui.market.list.state.* import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.delay private const val SHOW_MORE_KEY = "privacyPolicy" @Composable -internal fun TopBarWithSearch(searchBarUM: SearchBarUM) { +internal fun TopBarWithSearch(onBackClick: () -> Unit, onSearchClick: () -> Unit, marketsSearchBar: MarketsSearchBar) { val background = LocalMainBottomSheetColor.current.value - SearchBar( - modifier = Modifier - .drawBehind { drawRect(background) } - .padding(horizontal = 16.dp) - .padding(bottom = 12.dp), - state = searchBarUM, - colors = TangemSearchBarDefaults.defaultTextFieldColors.copy( - focusedContainerColor = TangemTheme.colors.field.focused, - unfocusedContainerColor = TangemTheme.colors.field.focused, - ), - ) + val focusRequester: FocusRequester = remember { FocusRequester() } + + AnimatedContent( + targetState = !marketsSearchBar.shouldAlwaysShowSearchBar && !marketsSearchBar.searchBarUM.isActive, + ) { showAppBarWithBackIcon -> + if (showAppBarWithBackIcon) { + AppBarWithBackButtonAndIcon( + onBackClick = onBackClick, + text = stringResourceSafe(R.string.markets_common_title), + iconRes = R.drawable.ic_search_24, + onIconClick = onSearchClick, + backgroundColor = background, + ) + } else { + SearchBar( + modifier = Modifier + .drawBehind { drawRect(background) } + .padding(horizontal = 16.dp) + .padding(bottom = 12.dp), + state = marketsSearchBar.searchBarUM, + colors = TangemSearchBarDefaults.defaultTextFieldColors.copy( + focusedContainerColor = TangemTheme.colors.field.focused, + unfocusedContainerColor = TangemTheme.colors.field.focused, + ), + focusRequester = focusRequester, + ) + LaunchedEffect(marketsSearchBar.shouldAlwaysShowSearchBar) { + if (marketsSearchBar.shouldAlwaysShowSearchBar) { + delay(timeMillis = 200) + focusRequester.requestFocus() + } + } + } + } } @Composable @@ -83,16 +110,22 @@ private fun ColumnScope.Content(state: MarketsListUM, modifier: Modifier = Modif Column(modifier.padding(horizontal = TangemTheme.dimens.size16)) { AnimatedVisibility( - visible = scrolledState.value.not(), + visible = scrolledState.value.not() && + state.isInSearchMode && + state.marketsSearchBar.searchBarUM.query.isNotEmpty(), ) { Column { SpacerH8() - Title(isInSearchMode = state.isInSearchMode) + Text( + text = stringResourceSafe(id = R.string.markets_search_result_title), + style = TangemTheme.typography.h3, + color = TangemTheme.colors.text.primary1, + ) SpacerH12() } } Column { - AnimatedVisibility(state.isInSearchMode.not()) { + AnimatedVisibility(!state.isInSearchMode && !state.marketsSearchBar.shouldAlwaysShowSearchBar) { Options( modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12), sortByTypeUM = state.selectedSortBy, @@ -161,20 +194,6 @@ private fun ColumnScope.Content(state: MarketsListUM, modifier: Modifier = Modif ) } -@Composable -private fun Title(isInSearchMode: Boolean, modifier: Modifier = Modifier) { - Text( - modifier = modifier, - text = if (isInSearchMode) { - stringResourceSafe(id = R.string.markets_search_result_title) - } else { - stringResourceSafe(id = R.string.markets_common_title) - }, - style = TangemTheme.typography.h3, - color = TangemTheme.colors.text.primary1, - ) -} - @Composable private fun Options( sortByTypeUM: SortByTypeUM, @@ -320,12 +339,15 @@ private fun Preview() { triggerScrollReset = consumedEvent(), onItemClick = {}, ), - searchBar = SearchBarUM( - placeholderText = resourceReference(R.string.markets_search_header_title), - query = "", - onQueryChange = {}, - isActive = false, - onActiveChange = { }, + marketsSearchBar = MarketsSearchBar( + searchBarUM = SearchBarUM( + placeholderText = resourceReference(R.string.markets_search_header_title), + query = "", + onQueryChange = {}, + isActive = false, + onActiveChange = { }, + ), + shouldAlwaysShowSearchBar = true, ), selectedSortBy = SortByTypeUM.Rating, selectedInterval = MarketsListUM.TrendInterval.H24, @@ -340,7 +362,7 @@ private fun Preview() { onClick = {}, onCloseClick = {}, ), - shouldAlwaysShowSearchBar = true, + onSearchClicked = {}, ), ) } diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/list/state/MarketsListUM.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/list/state/MarketsListUM.kt index 17fc25d214..9ae4f28516 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/list/state/MarketsListUM.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/list/state/MarketsListUM.kt @@ -13,17 +13,18 @@ import kotlinx.collections.immutable.ImmutableList internal data class MarketsListUM( val list: ListUM, - val searchBar: SearchBarUM, + val marketsSearchBar: MarketsSearchBar, val selectedSortBy: SortByTypeUM, val sortByBottomSheet: TangemBottomSheetConfig, val selectedInterval: TrendInterval, - val shouldAlwaysShowSearchBar: Boolean, val onIntervalClick: (TrendInterval) -> Unit, val onSortByButtonClick: () -> Unit, val marketsNotificationUM: MarketsNotificationUM?, + val onSearchClicked: () -> Unit, ) { val isInSearchMode - get() = searchBar.isActive + get() = marketsSearchBar.searchBarUM.isActive && + marketsSearchBar.searchBarUM.query.isNotEmpty() enum class TrendInterval(val text: TextReference) { H24(resourceReference(R.string.markets_selector_interval_24h_title)), @@ -32,7 +33,12 @@ internal data class MarketsListUM( } } -enum class SortByTypeUM(val text: TextReference) { +internal data class MarketsSearchBar( + val searchBarUM: SearchBarUM, + val shouldAlwaysShowSearchBar: Boolean, +) + +internal enum class SortByTypeUM(val text: TextReference) { Rating(resourceReference(R.string.markets_sort_by_rating_title)), Trending(resourceReference(R.string.markets_sort_by_trending_title)), ExperiencedBuyers(resourceReference(R.string.markets_sort_by_experienced_buyers_title)), @@ -43,7 +49,7 @@ enum class SortByTypeUM(val text: TextReference) { } @Immutable -sealed class ListUM { +internal sealed class ListUM { data class Content( val items: ImmutableList, diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/list/state/SortByBottomSheetContentUM.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/list/state/SortByBottomSheetContentUM.kt index 75e79d5844..947ee23844 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/list/state/SortByBottomSheetContentUM.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/list/state/SortByBottomSheetContentUM.kt @@ -2,7 +2,7 @@ package com.tangem.features.feed.ui.market.list.state import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent -data class SortByBottomSheetContentUM( +internal data class SortByBottomSheetContentUM( val selectedOption: SortByTypeUM, val onOptionClicked: (SortByTypeUM) -> Unit, ) : TangemBottomSheetConfigContent \ No newline at end of file