Updated on 2026-08-14
This commit is contained in:
parent
4ff20f3479
commit
0c5ff2eb37
14 changed files with 400 additions and 123 deletions
|
|
@ -86,14 +86,6 @@ internal class DefaultFeedEntryComponent @AssistedInject constructor(
|
|||
innerRouter.push(
|
||||
route = FeedEntryChildFactory.Child.TokenList(
|
||||
params = DefaultMarketsTokenListComponent.Params(
|
||||
onBackClicked = { onChildBack() },
|
||||
onTokenClick = { token, currency ->
|
||||
onMarketItemClick(
|
||||
token = token,
|
||||
appCurrency = currency,
|
||||
source = AnalyticsParam.ScreensSources.Market.value,
|
||||
)
|
||||
},
|
||||
preselectedSortType = sortBy ?: SortByTypeUM.Rating,
|
||||
shouldAlwaysShowSearchBar = sortBy == null,
|
||||
),
|
||||
|
|
@ -243,14 +235,6 @@ internal class DefaultFeedEntryComponent @AssistedInject constructor(
|
|||
)
|
||||
FeedEntryRoute.MarketTokenList -> FeedEntryChildFactory.Child.TokenList(
|
||||
DefaultMarketsTokenListComponent.Params(
|
||||
onBackClicked = { router.pop() },
|
||||
onTokenClick = { token, currency ->
|
||||
clickIntents.onMarketItemClick(
|
||||
token = token,
|
||||
appCurrency = currency,
|
||||
source = AnalyticsParam.ScreensSources.Market.value,
|
||||
)
|
||||
},
|
||||
preselectedSortType = SortByTypeUM.Rating,
|
||||
shouldAlwaysShowSearchBar = false,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ internal class FeedEntryChildFactory @Inject constructor(
|
|||
data object Search : Child
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
fun createChild(
|
||||
child: Child,
|
||||
appComponentContext: AppComponentContext,
|
||||
|
|
@ -82,6 +83,17 @@ internal class FeedEntryChildFactory @Inject constructor(
|
|||
DefaultMarketsTokenListComponent(
|
||||
appComponentContext = appComponentContext,
|
||||
params = child.params,
|
||||
clickIntents = DefaultMarketsTokenListComponent.ClickIntents(
|
||||
onBackClicked = onBackClicked,
|
||||
onSearchClicked = feedEntryClickIntents::openSearch,
|
||||
onTokenClick = { token, currency ->
|
||||
feedEntryClickIntents.onMarketItemClick(
|
||||
token = token,
|
||||
appCurrency = currency,
|
||||
source = AnalyticsParam.ScreensSources.Market.value,
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
is Child.NewsDetails -> {
|
||||
|
|
|
|||
|
|
@ -1,20 +1,34 @@
|
|||
package com.tangem.features.feed.components.market.list
|
||||
|
||||
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.core.decompose.context.AppComponentContext
|
||||
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.features.feed.model.market.list.MarketsListModel
|
||||
import com.tangem.features.feed.model.market.list.state.SortByTypeUM
|
||||
import com.tangem.features.feed.ui.components.FeedSearchBar
|
||||
import com.tangem.features.feed.ui.market.list.MarketsList
|
||||
import com.tangem.features.feed.ui.market.list.TopBarWithSearch
|
||||
import kotlinx.serialization.Serializable
|
||||
|
|
@ -22,20 +36,54 @@ import kotlinx.serialization.Serializable
|
|||
internal class DefaultMarketsTokenListComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
private val params: Params,
|
||||
private val clickIntents: ClickIntents,
|
||||
) : ComposableModularBottomSheetContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: MarketsListModel = getOrCreateModel<MarketsListModel, Params>(params = params)
|
||||
private val model: MarketsListModel = getOrCreateModel<MarketsListModel, ModelParams>(
|
||||
params = ModelParams(
|
||||
params = params,
|
||||
clickIntents = clickIntents,
|
||||
),
|
||||
)
|
||||
|
||||
@Composable
|
||||
override fun Title(bottomSheetState: State<BottomSheetState>) {
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
val bsState by bottomSheetState
|
||||
TopBarWithSearch(
|
||||
onBackClick = params.onBackClicked,
|
||||
onSearchClick = state.onSearchClicked,
|
||||
marketsSearchBar = state.marketsSearchBar,
|
||||
bottomSheetState = bsState,
|
||||
)
|
||||
val background = LocalMainBottomSheetColor.current.value
|
||||
|
||||
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 = clickIntents.onBackClicked,
|
||||
enabled = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
)
|
||||
.padding(TangemTheme.dimens2.x2),
|
||||
)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
TopBarWithSearch(
|
||||
onBackClick = clickIntents.onBackClicked,
|
||||
onSearchClick = state.onSearchClicked,
|
||||
marketsSearchBar = state.marketsSearchBar,
|
||||
bottomSheetState = bsState,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -62,9 +110,18 @@ internal class DefaultMarketsTokenListComponent(
|
|||
|
||||
@Serializable
|
||||
data class Params(
|
||||
val onBackClicked: () -> Unit,
|
||||
val onTokenClick: ((TokenMarketParams, AppCurrency) -> Unit),
|
||||
val preselectedSortType: SortByTypeUM,
|
||||
val shouldAlwaysShowSearchBar: Boolean,
|
||||
)
|
||||
|
||||
data class ClickIntents(
|
||||
val onBackClicked: () -> Unit,
|
||||
val onSearchClicked: () -> Unit,
|
||||
val onTokenClick: ((TokenMarketParams, AppCurrency) -> Unit),
|
||||
)
|
||||
|
||||
data class ModelParams(
|
||||
val params: Params,
|
||||
val clickIntents: ClickIntents,
|
||||
)
|
||||
}
|
||||
|
|
@ -45,7 +45,7 @@ internal class MarketsListModel @Inject constructor(
|
|||
|
||||
private val updateQuotesJob = JobHolder()
|
||||
|
||||
private val params = paramsContainer.require<DefaultMarketsTokenListComponent.Params>()
|
||||
private val modelParams = paramsContainer.require<DefaultMarketsTokenListComponent.ModelParams>()
|
||||
|
||||
private val currentAppCurrency = getSelectedAppCurrencyUseCase().map { maybeAppCurrency ->
|
||||
maybeAppCurrency.getOrElse { AppCurrency.Default }
|
||||
|
|
@ -65,10 +65,11 @@ internal class MarketsListModel @Inject constructor(
|
|||
onRetryButtonClicked = { activeListManager.reload() },
|
||||
onTokenClick = { onTokenUIClicked(it) },
|
||||
onShowTokensUnder100kClicked = { analyticsEventHandler.send(MarketsListAnalyticsEvent.ShowTokens()) },
|
||||
shouldAlwaysShowSearchBar = Provider { params.shouldAlwaysShowSearchBar },
|
||||
preselectedSortType = Provider { params.preselectedSortType },
|
||||
onBackClick = params.onBackClicked,
|
||||
shouldAlwaysShowSearchBar = Provider { modelParams.params.shouldAlwaysShowSearchBar },
|
||||
preselectedSortType = Provider { modelParams.params.preselectedSortType },
|
||||
onBackClick = modelParams.clickIntents.onBackClicked,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
onSearchBarClick = modelParams.clickIntents.onSearchClicked,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -261,7 +262,7 @@ internal class MarketsListModel @Inject constructor(
|
|||
private fun onTokenUIClicked(token: MarketsListItemUM) {
|
||||
modelScope.launch {
|
||||
activeListManager.getTokenById(token.id)?.let { found ->
|
||||
params.onTokenClick(found.toSerializableParam(), currentAppCurrency.value)
|
||||
modelParams.clickIntents.onTokenClick(found.toSerializableParam(), currentAppCurrency.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.tangem.core.ui.event.StateEvent
|
|||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.feed.ui.feed.state.FeedListSearchBar
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal data class MarketsListUM(
|
||||
|
|
@ -20,6 +21,8 @@ internal data class MarketsListUM(
|
|||
val onIntervalClick: (TrendInterval) -> Unit,
|
||||
val onSortByButtonClick: () -> Unit,
|
||||
val onSearchClicked: () -> Unit,
|
||||
val feedListSearchBar: FeedListSearchBar,
|
||||
val sortByMenuUM: SortByMenuUM,
|
||||
) {
|
||||
val isInSearchMode
|
||||
get() = marketsSearchBar.searchBarUM.query.isNotEmpty()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.features.feed.model.market.list.state
|
||||
|
||||
internal data class SortByMenuUM(
|
||||
val selectedOption: SortByTypeUM,
|
||||
val onOptionClicked: (SortByTypeUM) -> Unit,
|
||||
)
|
||||
|
|
@ -12,6 +12,7 @@ import com.tangem.domain.models.currency.CryptoCurrency
|
|||
import com.tangem.features.feed.impl.R
|
||||
import com.tangem.features.feed.model.feed.analytics.FeedAnalyticsEvent
|
||||
import com.tangem.features.feed.model.market.list.state.*
|
||||
import com.tangem.features.feed.ui.feed.state.FeedListSearchBar
|
||||
import com.tangem.utils.Provider
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
@ -33,6 +34,7 @@ internal class MarketsListUMStateManager(
|
|||
private val onShowTokensUnder100kClicked: () -> Unit,
|
||||
private val onBackClick: () -> Unit,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val onSearchBarClick: () -> Unit,
|
||||
) {
|
||||
|
||||
val state = MutableStateFlow(state())
|
||||
|
|
@ -64,6 +66,9 @@ internal class MarketsListUMStateManager(
|
|||
selectedOption = value,
|
||||
),
|
||||
),
|
||||
sortByMenuUM = marketsListUM.sortByMenuUM.copy(
|
||||
selectedOption = value,
|
||||
),
|
||||
list = if (marketsListUM.list is ListUM.Content && marketsListUM.selectedSortBy != value) {
|
||||
marketsListUM.list.copy(
|
||||
triggerScrollReset = triggeredEvent(Unit) { consumeTriggerResetScrollEvent() },
|
||||
|
|
@ -232,16 +237,24 @@ internal class MarketsListUMStateManager(
|
|||
onDismissRequest = { isSortByBottomSheetShown = false },
|
||||
content = SortByBottomSheetContentUM(
|
||||
selectedOption = preselectedSortType(),
|
||||
onOptionClicked = ::onBottomSheetOptionClicked,
|
||||
onOptionClicked = ::onBottomSheetOrMenuOptionClicked,
|
||||
),
|
||||
),
|
||||
onSearchClicked = {
|
||||
analyticsEventHandler.send(FeedAnalyticsEvent.TokenSearchedClicked())
|
||||
changeSearchBarIsActive(true)
|
||||
},
|
||||
feedListSearchBar = FeedListSearchBar(
|
||||
onBarClick = onSearchBarClick,
|
||||
placeholderText = resourceReference(id = R.string.markets_search_title_placeholder),
|
||||
),
|
||||
sortByMenuUM = SortByMenuUM(
|
||||
selectedOption = preselectedSortType(),
|
||||
onOptionClicked = ::onBottomSheetOrMenuOptionClicked,
|
||||
),
|
||||
)
|
||||
|
||||
private fun onBottomSheetOptionClicked(sortByTypeUM: SortByTypeUM) {
|
||||
private fun onBottomSheetOrMenuOptionClicked(sortByTypeUM: SortByTypeUM) {
|
||||
state.update { marketsListUM ->
|
||||
marketsListUM.copy(
|
||||
selectedSortBy = sortByTypeUM,
|
||||
|
|
@ -251,6 +264,7 @@ internal class MarketsListUMStateManager(
|
|||
selectedOption = sortByTypeUM,
|
||||
),
|
||||
),
|
||||
sortByMenuUM = marketsListUM.sortByMenuUM.copy(selectedOption = sortByTypeUM),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,20 +111,23 @@ private fun InsightsBlockV1(state: InsightsUM, modifier: Modifier = Modifier) {
|
|||
|
||||
@Composable
|
||||
private fun InsightsBlockV2(state: InsightsUM, modifier: Modifier = Modifier) {
|
||||
val segmentItems = persistentListOf(
|
||||
TangemSegmentUM(
|
||||
id = PriceChangeInterval.H24.name,
|
||||
title = resourceReference(R.string.markets_token_details_insight_day_timeline),
|
||||
),
|
||||
TangemSegmentUM(
|
||||
id = PriceChangeInterval.WEEK.name,
|
||||
title = resourceReference(R.string.markets_token_details_insight_week_timeline),
|
||||
),
|
||||
TangemSegmentUM(
|
||||
id = PriceChangeInterval.MONTH.name,
|
||||
title = resourceReference(R.string.markets_token_details_insight_month_timeline),
|
||||
),
|
||||
)
|
||||
val segmentItems = remember {
|
||||
persistentListOf(
|
||||
TangemSegmentUM(
|
||||
id = PriceChangeInterval.H24.name,
|
||||
title = resourceReference(R.string.markets_token_details_insight_day_timeline),
|
||||
),
|
||||
TangemSegmentUM(
|
||||
id = PriceChangeInterval.WEEK.name,
|
||||
title = resourceReference(R.string.markets_token_details_insight_week_timeline),
|
||||
),
|
||||
TangemSegmentUM(
|
||||
id = PriceChangeInterval.MONTH.name,
|
||||
title = resourceReference(R.string.markets_token_details_insight_month_timeline),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
var currentInterval by remember { mutableStateOf(segmentItems.first()) }
|
||||
|
||||
TokenMarketInformationBlock(
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.*
|
|||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.Text
|
||||
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
|
||||
|
|
@ -24,27 +23,27 @@ 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.bottomsheets.state.BottomSheetState
|
||||
import com.tangem.core.ui.components.buttons.SecondarySmallButton
|
||||
import com.tangem.core.ui.components.buttons.SmallButtonConfig
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
|
||||
import com.tangem.core.ui.components.buttons.segmentedbutton.SegmentedButtons
|
||||
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.components.haze.hazeSourceTangem
|
||||
import com.tangem.core.ui.components.keyboardAsState
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.conditionalCompose
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
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.core.ui.res.TangemThemePreview
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.feed.impl.R
|
||||
import com.tangem.features.feed.model.market.list.state.*
|
||||
import com.tangem.features.feed.ui.feed.state.FeedListSearchBar
|
||||
import com.tangem.features.feed.ui.market.list.components.MarketsListLazyColumn
|
||||
import com.tangem.features.feed.ui.market.list.components.MarketsListSortByBottomSheet
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import com.tangem.features.feed.ui.market.list.components.Options
|
||||
import dev.chrisbanes.haze.rememberHazeState
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
|
|
@ -123,7 +122,16 @@ internal fun MarketsList(state: MarketsListUM, modifier: Modifier = Modifier) {
|
|||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun ColumnScope.Content(state: MarketsListUM, modifier: Modifier = Modifier) {
|
||||
val strokeColor = TangemTheme.colors.stroke.primary
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
|
||||
val hazeState = rememberHazeState()
|
||||
|
||||
val strokeColor = if (isRedesignEnabled) {
|
||||
TangemTheme.colors2.border.neutral.primary
|
||||
} else {
|
||||
TangemTheme.colors.stroke.primary
|
||||
}
|
||||
|
||||
val scrolledState = remember { mutableStateOf(false) }
|
||||
|
||||
Column(modifier.padding(horizontal = TangemTheme.dimens.size16)) {
|
||||
|
|
@ -145,11 +153,19 @@ private fun ColumnScope.Content(state: MarketsListUM, modifier: Modifier = Modif
|
|||
Column {
|
||||
AnimatedVisibility(!state.isInSearchMode && !state.marketsSearchBar.shouldAlwaysShowSearchBar) {
|
||||
Options(
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12),
|
||||
modifier = Modifier.padding(
|
||||
bottom = if (isRedesignEnabled) {
|
||||
TangemTheme.dimens2.x2
|
||||
} else {
|
||||
TangemTheme.dimens.spacing12
|
||||
},
|
||||
),
|
||||
sortByTypeUM = state.selectedSortBy,
|
||||
trendInterval = state.selectedInterval,
|
||||
onIntervalClick = state.onIntervalClick,
|
||||
onSortByClick = state.onSortByButtonClick,
|
||||
sortMenuUM = state.sortByMenuUM,
|
||||
hazeState = hazeState,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -172,65 +188,18 @@ private fun ColumnScope.Content(state: MarketsListUM, modifier: Modifier = Modif
|
|||
},
|
||||
)
|
||||
ItemsList(
|
||||
modifier = Modifier.conditionalCompose(
|
||||
condition = isRedesignEnabled,
|
||||
modifier = {
|
||||
hazeSourceTangem(zIndex = 0f, state = hazeState)
|
||||
},
|
||||
),
|
||||
scrolledState = scrolledState,
|
||||
isInSearchMode = state.isInSearchMode,
|
||||
state = state.list,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Options(
|
||||
sortByTypeUM: SortByTypeUM,
|
||||
trendInterval: MarketsListUM.TrendInterval,
|
||||
onSortByClick: () -> Unit,
|
||||
onIntervalClick: (MarketsListUM.TrendInterval) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.height(IntrinsicSize.Max)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
SecondarySmallButton(
|
||||
config = SmallButtonConfig(
|
||||
text = sortByTypeUM.text,
|
||||
onClick = onSortByClick,
|
||||
icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_chevron_24),
|
||||
),
|
||||
)
|
||||
SegmentedButtons(
|
||||
config = persistentListOf(
|
||||
MarketsListUM.TrendInterval.H24,
|
||||
MarketsListUM.TrendInterval.D7,
|
||||
MarketsListUM.TrendInterval.M1,
|
||||
),
|
||||
color = TangemTheme.colors.button.secondary,
|
||||
initialSelectedItem = trendInterval,
|
||||
onClick = onIntervalClick,
|
||||
modifier = Modifier
|
||||
.width(160.dp)
|
||||
.fillMaxHeight(),
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.align(Alignment.Center)
|
||||
.padding(
|
||||
vertical = TangemTheme.dimens.spacing4,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
text = it.text.resolveReference(),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ItemsList(
|
||||
scrolledState: MutableState<Boolean>,
|
||||
|
|
@ -343,6 +312,14 @@ private fun Preview() {
|
|||
content = SortByBottomSheetContentUM(selectedOption = SortByTypeUM.Rating) {},
|
||||
),
|
||||
onSearchClicked = {},
|
||||
feedListSearchBar = FeedListSearchBar(
|
||||
onBarClick = {},
|
||||
placeholderText = resourceReference(id = R.string.markets_search_title_placeholder),
|
||||
),
|
||||
sortByMenuUM = SortByMenuUM(
|
||||
selectedOption = SortByTypeUM.Rating,
|
||||
onOptionClicked = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.features.feed.ui.market.list.components
|
|||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
|
|
@ -11,6 +11,7 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.markets.MarketsListItem
|
||||
import com.tangem.common.ui.markets.MarketsListItemPlaceholder
|
||||
import com.tangem.common.ui.markets.models.MarketsListItemUM.Companion.TOKEN_LAZY_LIST_ID_SEPARATOR
|
||||
|
|
@ -19,9 +20,12 @@ import com.tangem.core.ui.components.UnableToLoadData
|
|||
import com.tangem.core.ui.components.buttons.SecondarySmallButton
|
||||
import com.tangem.core.ui.components.buttons.SmallButtonConfig
|
||||
import com.tangem.core.ui.components.list.InfiniteListHandler
|
||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.event.EventEffect
|
||||
import com.tangem.core.ui.extensions.conditionalCompose
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.test.MarketsTestTags
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
|
|
@ -40,6 +44,7 @@ internal fun MarketsListLazyColumn(
|
|||
lazyListState: LazyListState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
|
|
@ -93,11 +98,22 @@ internal fun MarketsListLazyColumn(
|
|||
}
|
||||
}
|
||||
is ListUM.Content -> {
|
||||
items(
|
||||
itemsIndexed(
|
||||
items = state.items,
|
||||
key = { it.getComposeKey() },
|
||||
) { item ->
|
||||
key = { _, item -> item.getComposeKey() },
|
||||
) { index, item ->
|
||||
MarketsListItem(
|
||||
modifier = Modifier.conditionalCompose(
|
||||
condition = isRedesignEnabled,
|
||||
modifier = {
|
||||
roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = state.items.lastIndex,
|
||||
backgroundColor = TangemTheme.colors2.surface.level3,
|
||||
radius = TangemTheme.dimens2.x5,
|
||||
)
|
||||
},
|
||||
),
|
||||
model = item,
|
||||
onClick = { state.onItemClick(item) },
|
||||
)
|
||||
|
|
@ -144,8 +160,8 @@ private fun LoadingErrorItem(onTryAgain: () -> Unit, modifier: Modifier = Modifi
|
|||
Box(
|
||||
modifier
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing16,
|
||||
vertical = TangemTheme.dimens.spacing12,
|
||||
horizontal = 16.dp,
|
||||
vertical = 12.dp,
|
||||
)
|
||||
.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,176 @@
|
|||
package com.tangem.features.feed.ui.market.list.components
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.buttons.SecondarySmallButton
|
||||
import com.tangem.core.ui.components.buttons.SmallButtonConfig
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
|
||||
import com.tangem.core.ui.components.buttons.segmentedbutton.SegmentedButtons
|
||||
import com.tangem.core.ui.components.haze.hazeEffectTangem
|
||||
import com.tangem.core.ui.ds.button.PrimaryInverseTangemButton
|
||||
import com.tangem.core.ui.ds.button.TangemButtonShape
|
||||
import com.tangem.core.ui.ds.button.TangemButtonSize
|
||||
import com.tangem.core.ui.ds.tabs.TangemSegmentUM
|
||||
import com.tangem.core.ui.ds.tabs.TangemSegmentedPicker
|
||||
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.impl.R
|
||||
import com.tangem.features.feed.model.market.list.state.MarketsListUM
|
||||
import com.tangem.features.feed.model.market.list.state.SortByMenuUM
|
||||
import com.tangem.features.feed.model.market.list.state.SortByTypeUM
|
||||
import dev.chrisbanes.haze.HazeState
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import com.tangem.core.ui.ds.button.TangemButtonIconPosition as RedesignTangemButtonIconPosition
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
internal fun Options(
|
||||
sortMenuUM: SortByMenuUM,
|
||||
sortByTypeUM: SortByTypeUM,
|
||||
trendInterval: MarketsListUM.TrendInterval,
|
||||
hazeState: HazeState,
|
||||
onSortByClick: () -> Unit,
|
||||
onIntervalClick: (MarketsListUM.TrendInterval) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
OptionsV2(
|
||||
sortMenuUM = sortMenuUM,
|
||||
trendInterval = trendInterval,
|
||||
onIntervalClick = onIntervalClick,
|
||||
modifier = modifier,
|
||||
hazeState = hazeState,
|
||||
)
|
||||
} else {
|
||||
OptionsV1(
|
||||
sortByTypeUM = sortByTypeUM,
|
||||
trendInterval = trendInterval,
|
||||
onSortByClick = onSortByClick,
|
||||
onIntervalClick = onIntervalClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OptionsV1(
|
||||
sortByTypeUM: SortByTypeUM,
|
||||
trendInterval: MarketsListUM.TrendInterval,
|
||||
onSortByClick: () -> Unit,
|
||||
onIntervalClick: (MarketsListUM.TrendInterval) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.height(IntrinsicSize.Max)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
SecondarySmallButton(
|
||||
config = SmallButtonConfig(
|
||||
text = sortByTypeUM.text,
|
||||
onClick = onSortByClick,
|
||||
icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_chevron_24),
|
||||
),
|
||||
)
|
||||
SegmentedButtons(
|
||||
config = persistentListOf(
|
||||
MarketsListUM.TrendInterval.H24,
|
||||
MarketsListUM.TrendInterval.D7,
|
||||
MarketsListUM.TrendInterval.M1,
|
||||
),
|
||||
color = TangemTheme.colors.button.secondary,
|
||||
initialSelectedItem = trendInterval,
|
||||
onClick = onIntervalClick,
|
||||
modifier = Modifier
|
||||
.width(160.dp)
|
||||
.fillMaxHeight(),
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.align(Alignment.Center)
|
||||
.padding(
|
||||
vertical = TangemTheme.dimens.spacing4,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
text = it.text.resolveReference(),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OptionsV2(
|
||||
sortMenuUM: SortByMenuUM,
|
||||
trendInterval: MarketsListUM.TrendInterval,
|
||||
hazeState: HazeState,
|
||||
onIntervalClick: (MarketsListUM.TrendInterval) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var isShowDropdownMenu by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
val segmentItems = remember {
|
||||
persistentListOf(
|
||||
TangemSegmentUM(
|
||||
id = MarketsListUM.TrendInterval.H24.name,
|
||||
title = MarketsListUM.TrendInterval.H24.text,
|
||||
),
|
||||
TangemSegmentUM(
|
||||
id = MarketsListUM.TrendInterval.D7.name,
|
||||
title = MarketsListUM.TrendInterval.D7.text,
|
||||
),
|
||||
TangemSegmentUM(
|
||||
id = MarketsListUM.TrendInterval.M1.name,
|
||||
title = MarketsListUM.TrendInterval.M1.text,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.height(IntrinsicSize.Max)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
PrimaryInverseTangemButton(
|
||||
onClick = {
|
||||
isShowDropdownMenu = true
|
||||
},
|
||||
iconPosition = RedesignTangemButtonIconPosition.End,
|
||||
iconRes = R.drawable.ic_chewron_down_20,
|
||||
text = sortMenuUM.selectedOption.text,
|
||||
size = TangemButtonSize.X9,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
)
|
||||
|
||||
TangemSegmentedPicker(
|
||||
items = segmentItems,
|
||||
initialSelectedItem = segmentItems.firstOrNull { it.id == trendInterval.name },
|
||||
isFixed = false,
|
||||
isAltSurface = true,
|
||||
minSegmentWidth = 54.dp,
|
||||
onClick = { segment -> onIntervalClick(MarketsListUM.TrendInterval.valueOf(segment.id)) },
|
||||
)
|
||||
}
|
||||
|
||||
SortByMenu(
|
||||
sortMenuUM = sortMenuUM,
|
||||
showDropdownMenu = isShowDropdownMenu,
|
||||
onDropdownDismiss = { isShowDropdownMenu = false },
|
||||
modifier = Modifier.hazeEffectTangem(hazeState) {
|
||||
blurRadius = 10.dp
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tangem.features.feed.ui.market.list.components
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import com.tangem.core.ui.ds.contextmenu.TangemContextMenu
|
||||
import com.tangem.core.ui.ds.contextmenu.TangemContextMenuCheckboxItem
|
||||
import com.tangem.features.feed.model.market.list.state.SortByMenuUM
|
||||
import com.tangem.features.feed.model.market.list.state.SortByTypeUM
|
||||
|
||||
@Composable
|
||||
internal fun SortByMenu(
|
||||
sortMenuUM: SortByMenuUM,
|
||||
showDropdownMenu: Boolean,
|
||||
onDropdownDismiss: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
TangemContextMenu(
|
||||
expanded = showDropdownMenu,
|
||||
onDismissRequest = onDropdownDismiss,
|
||||
offset = DpOffset.Zero,
|
||||
modifier = modifier,
|
||||
) {
|
||||
SortByTypeUM.entries.fastForEach { sortType ->
|
||||
TangemContextMenuCheckboxItem(
|
||||
title = sortType.text,
|
||||
isChecked = sortMenuUM.selectedOption == sortType,
|
||||
onClick = {
|
||||
sortMenuUM.onOptionClicked(sortType)
|
||||
onDropdownDismiss()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,11 +13,9 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.UnableToLoadData
|
||||
import com.tangem.core.ui.components.haze.hazeSourceTangem
|
||||
import com.tangem.core.ui.components.pager.PagerIndicator
|
||||
import com.tangem.core.ui.ds.TangemPagerIndicator
|
||||
import com.tangem.core.ui.ds.TangemPagerIndicatorColors
|
||||
import com.tangem.core.ui.extensions.conditionalCompose
|
||||
import com.tangem.core.ui.res.*
|
||||
import com.tangem.features.feed.ui.news.details.components.ArticleDetail
|
||||
import com.tangem.features.feed.ui.news.details.components.NewsDetailsPlaceholder
|
||||
|
|
@ -84,12 +82,6 @@ private fun Content(state: NewsDetailsUM, background: Color) {
|
|||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.conditionalCompose(
|
||||
condition = isRedesignEnabled,
|
||||
modifier = {
|
||||
hazeSourceTangem(zIndex = 1f)
|
||||
},
|
||||
)
|
||||
.background(background),
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue