Updated on 2026-08-14
This commit is contained in:
parent
27fd5458d1
commit
6b9aacb1ea
18 changed files with 235 additions and 132 deletions
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -8,4 +8,5 @@ data class SearchBarUM(
|
|||
val onQueryChange: (String) -> Unit,
|
||||
val isActive: Boolean,
|
||||
val onActiveChange: (Boolean) -> Unit,
|
||||
val onClearClick: () -> Unit = {},
|
||||
)
|
||||
18
core/ui/src/main/res/drawable/ic_show_more_news_48.xml
Normal file
18
core/ui/src/main/res/drawable/ic_show_more_news_48.xml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M24,0L24,0A24,24 0,0 1,48 24L48,24A24,24 0,0 1,24 48L24,48A24,24 0,0 1,0 24L0,24A24,24 0,0 1,24 0z"/>
|
||||
<path
|
||||
android:pathData="M24,0L24,0A24,24 0,0 1,48 24L48,24A24,24 0,0 1,24 48L24,48A24,24 0,0 1,0 24L0,24A24,24 0,0 1,24 0z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillColor="#0099FF"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:pathData="M15.782,24.005C15.781,23.508 16.184,23.105 16.681,23.104L29.128,23.099L23.92,17.891C23.569,17.539 23.569,16.97 23.92,16.618C24.272,16.267 24.842,16.267 25.193,16.618L31.943,23.368C32.295,23.72 32.295,24.289 31.943,24.641L25.193,31.391C24.842,31.742 24.272,31.742 23.92,31.391C23.569,31.039 23.569,30.47 23.92,30.118L29.139,24.899L16.682,24.904C16.185,24.905 15.782,24.502 15.782,24.005Z"
|
||||
android:fillColor="#0099FF"/>
|
||||
</group>
|
||||
</vector>
|
||||
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ internal class DefaultFeedComponent(
|
|||
@Composable
|
||||
override fun Title() {
|
||||
val state by feedComponentModel.state.collectAsStateWithLifecycle()
|
||||
FeedListHeader(state.searchBar)
|
||||
FeedListHeader(state.feedListSearchBar)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<FeedListUM>
|
||||
field = MutableStateFlow<FeedListUM>(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 ->
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = {},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
package com.tangem.features.feed.ui.feed.state
|
||||
|
||||
import com.tangem.utils.Provider
|
||||
|
||||
internal class SearchBarStateFactory(
|
||||
private val currentStateProvider: Provider<FeedListUM>,
|
||||
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(),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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 = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<MarketsListItemUM>,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue