Updated on 2026-08-14
This commit is contained in:
parent
a94db85dfd
commit
648ceb9946
9 changed files with 94 additions and 60 deletions
|
|
@ -4,8 +4,13 @@ import androidx.compose.foundation.lazy.LazyListState
|
|||
import androidx.compose.runtime.*
|
||||
|
||||
@Composable
|
||||
fun InfiniteListHandler(listState: LazyListState, onLoadMore: () -> Boolean, buffer: Int = 2) {
|
||||
val loadMore by remember {
|
||||
fun InfiniteListHandler(
|
||||
listState: LazyListState,
|
||||
onLoadMore: () -> Boolean,
|
||||
buffer: Int = 2,
|
||||
triggerLoadMoreCheckOnItemsCountChange: Boolean = false,
|
||||
) {
|
||||
val loadMore by remember(buffer, listState) {
|
||||
derivedStateOf {
|
||||
val layoutInfo = listState.layoutInfo
|
||||
val totalItemsNumber = layoutInfo.totalItemsCount
|
||||
|
|
@ -15,12 +20,18 @@ fun InfiniteListHandler(listState: LazyListState, onLoadMore: () -> Boolean, buf
|
|||
}
|
||||
}
|
||||
|
||||
val totalItemsCount by remember { derivedStateOf { listState.layoutInfo.totalItemsCount } }
|
||||
var emitted by remember(totalItemsCount) { mutableStateOf(false) }
|
||||
val totalItemsCount by remember(listState) { derivedStateOf { listState.layoutInfo.totalItemsCount } }
|
||||
var emitted by remember(totalItemsCount, buffer, listState) { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(loadMore) {
|
||||
if (loadMore && !emitted) {
|
||||
emitted = onLoadMore()
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(totalItemsCount) {
|
||||
if (triggerLoadMoreCheckOnItemsCountChange && loadMore && !emitted) {
|
||||
emitted = onLoadMore()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import com.tangem.datasource.api.common.response.getOrThrow
|
|||
import com.tangem.datasource.api.markets.TangemTechMarketsApi
|
||||
import com.tangem.datasource.api.markets.models.response.TokenMarketChartListResponse
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi.Companion.marketsQuoteFields
|
||||
import com.tangem.domain.markets.TokenMarket
|
||||
import com.tangem.domain.markets.TokenMarketUpdateRequest
|
||||
import com.tangem.pagination.Batch
|
||||
|
|
@ -70,7 +71,7 @@ internal class MarketsBatchUpdateFetcher(
|
|||
tangemTechApi.getQuotes(
|
||||
currencyId = updateRequest.currencyId,
|
||||
coinIds = idsToUpdate.map { it.second }.flatten().joinToString(separator = ","),
|
||||
fields = quoteFields.joinToString(separator = ","),
|
||||
fields = marketsQuoteFields.joinToString(separator = ","),
|
||||
).getOrThrow()
|
||||
}
|
||||
}
|
||||
|
|
@ -120,13 +121,4 @@ internal class MarketsBatchUpdateFetcher(
|
|||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val quoteFields = listOf(
|
||||
"price",
|
||||
"priceChange24h",
|
||||
"priceChange1w",
|
||||
"priceChange30d",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,6 @@ import com.tangem.features.markets.tokenlist.impl.ui.state.MarketsListUM
|
|||
import com.tangem.features.markets.tokenlist.impl.ui.state.SortByTypeUM
|
||||
import com.tangem.utils.Provider
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
|
|
@ -111,54 +110,55 @@ internal class MarketsListUMStateManager(
|
|||
|
||||
private fun MarketsListUM.updateItems(newItems: ImmutableList<MarketsListItemUM>): MarketsListUM {
|
||||
val currentState = this
|
||||
val isNextPageInSearch = isInSearchState && (this.list as? ListUM.Content)?.showUnder100kTokens == true
|
||||
var searchUiItemsCached: ImmutableList<MarketsListItemUM> = persistentListOf()
|
||||
|
||||
val items = when {
|
||||
isInSearchState && isNextPageInSearch.not() -> {
|
||||
searchUiItemsCached = newItems
|
||||
val filtered = newItems.filter { item -> item.isUnder100kMarketCap.not() }.toImmutableList()
|
||||
if (isInSearchMode.not() || currentState.showUnder100kButtonAlreadyPressed()) {
|
||||
val itemsWithFilteredPriceChange = newItems.filterPriceChangeByVisibility()
|
||||
|
||||
if (filtered.size == newItems.size) {
|
||||
return currentState.copy(list = generalContentState(newItems))
|
||||
} else {
|
||||
filtered
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
searchUiItemsCached = persistentListOf()
|
||||
newItems
|
||||
}
|
||||
return currentState.copy(
|
||||
list = generalContentState(itemsWithFilteredPriceChange)
|
||||
.copy(
|
||||
showUnder100kTokensNotificationWasHidden = currentState.showUnder100kButtonAlreadyPressed(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
val itemsWithFilteredPriceChange = items.filterPriceChangeByVisibility()
|
||||
// Search state cases
|
||||
|
||||
return currentState.copy(
|
||||
list = ListUM.Content(
|
||||
items = itemsWithFilteredPriceChange,
|
||||
loadMore = onLoadMoreUiItems,
|
||||
visibleIdsChanged = visibleItemsChanged,
|
||||
showUnder100kTokens = isInSearchState.not() || isNextPageInSearch,
|
||||
onShowTokensUnder100kClicked = {
|
||||
if (searchUiItemsCached.isNotEmpty()) {
|
||||
val filtered = newItems.filter { item -> item.isUnder100kMarketCap.not() }
|
||||
.toImmutableList()
|
||||
.filterPriceChangeByVisibility()
|
||||
|
||||
if (filtered.size != newItems.size) {
|
||||
val searchUiItemsCached = newItems.filterPriceChangeByVisibility()
|
||||
|
||||
return currentState.copy(
|
||||
list = generalContentState(filtered).copy(
|
||||
showUnder100kTokensNotificationWasHidden = false,
|
||||
showUnder100kTokensNotification = true,
|
||||
onShowTokensUnder100kClicked = {
|
||||
state.update { s ->
|
||||
if (s.list is ListUM.Content) {
|
||||
(s.list as? ListUM.Content)?.let {
|
||||
s.copy(
|
||||
list = s.list.copy(
|
||||
items = searchUiItemsCached,
|
||||
showUnder100kTokens = true,
|
||||
showUnder100kTokensNotification = false,
|
||||
showUnder100kTokensNotificationWasHidden = true,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
s
|
||||
}
|
||||
} ?: s
|
||||
}
|
||||
}
|
||||
},
|
||||
triggerScrollReset = consumedEvent(),
|
||||
onItemClick = onTokenClick,
|
||||
),
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
} else {
|
||||
return currentState.copy(
|
||||
list = generalContentState(newItems.filterPriceChangeByVisibility()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MarketsListUM.showUnder100kButtonAlreadyPressed(): Boolean {
|
||||
return this.list is ListUM.Content && this.isInSearchMode && this.list.showUnder100kTokensNotificationWasHidden
|
||||
}
|
||||
|
||||
// Show price change animation for visible items only
|
||||
|
|
@ -182,10 +182,11 @@ internal class MarketsListUMStateManager(
|
|||
items = newItems,
|
||||
loadMore = onLoadMoreUiItems,
|
||||
visibleIdsChanged = visibleItemsChanged,
|
||||
showUnder100kTokens = true,
|
||||
showUnder100kTokensNotification = false,
|
||||
onShowTokensUnder100kClicked = {},
|
||||
triggerScrollReset = consumedEvent(),
|
||||
onItemClick = onTokenClick,
|
||||
showUnder100kTokensNotificationWasHidden = false,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -301,7 +301,8 @@ private fun Preview() {
|
|||
item.copy(id = index.toString())
|
||||
}
|
||||
.toImmutableList(),
|
||||
showUnder100kTokens = false,
|
||||
showUnder100kTokensNotification = false,
|
||||
showUnder100kTokensNotificationWasHidden = false,
|
||||
loadMore = {},
|
||||
visibleIdsChanged = {},
|
||||
onShowTokensUnder100kClicked = {},
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import com.tangem.features.markets.tokenlist.impl.ui.state.ListUM
|
|||
import kotlinx.coroutines.launch
|
||||
|
||||
private const val LOAD_NEXT_PAGE_ON_END_INDEX = 50
|
||||
private const val LOAD_NEXT_PAGE_ON_END_INDEX_SEARCH = 25
|
||||
private const val TOKEN_LAZY_LIST_ID_SEPARATOR = "***"
|
||||
|
||||
@Composable
|
||||
|
|
@ -95,7 +96,7 @@ internal fun MarketsListLazyColumn(
|
|||
)
|
||||
}
|
||||
|
||||
if (isInSearchMode && state.showUnder100kTokens.not()) {
|
||||
if (isInSearchMode && state.showUnder100kTokensNotification) {
|
||||
item(key = "show tokens under 100k".hashCode()) {
|
||||
ShowTokensUnder100kItem(
|
||||
onShowTokensClick = state.onShowTokensUnder100kClicked,
|
||||
|
|
@ -112,10 +113,15 @@ internal fun MarketsListLazyColumn(
|
|||
|
||||
InfiniteListHandler(
|
||||
listState = lazyListState,
|
||||
buffer = LOAD_NEXT_PAGE_ON_END_INDEX,
|
||||
buffer = if (isInSearchMode) {
|
||||
LOAD_NEXT_PAGE_ON_END_INDEX_SEARCH
|
||||
} else {
|
||||
LOAD_NEXT_PAGE_ON_END_INDEX
|
||||
},
|
||||
triggerLoadMoreCheckOnItemsCountChange = true,
|
||||
onLoadMore = remember(state) {
|
||||
{
|
||||
if (state is ListUM.Content && state.showUnder100kTokens) {
|
||||
if (state is ListUM.Content && state.showUnder100kTokensNotification.not()) {
|
||||
state.loadMore()
|
||||
true
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ internal class MarketChartListItemPreviewDataProvider : CollectionPreviewParamet
|
|||
chardData = MarketChartRawData(
|
||||
y = persistentListOf(0.4, 0.2, 0.4, 0.1, 0.4, 2.0, 5.0, 0.1, 2.0, 2.0, 3.0),
|
||||
),
|
||||
isUnder100kMarketCap = false,
|
||||
),
|
||||
MarketsListItemUM(
|
||||
id = "1",
|
||||
|
|
@ -34,6 +35,7 @@ internal class MarketChartListItemPreviewDataProvider : CollectionPreviewParamet
|
|||
trendPercentText = "12.43%",
|
||||
trendType = PriceChangeType.NEUTRAL,
|
||||
chardData = null,
|
||||
isUnder100kMarketCap = false,
|
||||
),
|
||||
MarketsListItemUM(
|
||||
id = "1",
|
||||
|
|
@ -48,6 +50,7 @@ internal class MarketChartListItemPreviewDataProvider : CollectionPreviewParamet
|
|||
chardData = MarketChartRawData(
|
||||
y = persistentListOf(0.4, 0.2, 0.4, 0.1, 0.4, 2.0, 5.0, 0.1, 2.0, 2.0, 3.0),
|
||||
),
|
||||
isUnder100kMarketCap = false,
|
||||
),
|
||||
MarketsListItemUM(
|
||||
id = "1",
|
||||
|
|
@ -62,6 +65,7 @@ internal class MarketChartListItemPreviewDataProvider : CollectionPreviewParamet
|
|||
chardData = MarketChartRawData(
|
||||
y = persistentListOf(0.4, 0.2, 0.4, 0.1, 0.4, 2.0, 5.0, 0.1, 2.0, 2.0, 3.0),
|
||||
),
|
||||
isUnder100kMarketCap = false,
|
||||
),
|
||||
MarketsListItemUM(
|
||||
id = "1",
|
||||
|
|
@ -76,6 +80,7 @@ internal class MarketChartListItemPreviewDataProvider : CollectionPreviewParamet
|
|||
chardData = MarketChartRawData(
|
||||
y = persistentListOf(0.4, 0.2, 0.4, 0.1, 0.4, 2.0, 5.0, 0.1, 2.0, 2.0, 3.0),
|
||||
),
|
||||
isUnder100kMarketCap = false,
|
||||
),
|
||||
MarketsListItemUM(
|
||||
id = "1",
|
||||
|
|
@ -90,6 +95,7 @@ internal class MarketChartListItemPreviewDataProvider : CollectionPreviewParamet
|
|||
chardData = MarketChartRawData(
|
||||
y = persistentListOf(0.4, 0.2, 0.4, 0.1, 0.4, 2.0, 5.0, 0.1, 2.0, 2.0, 3.0),
|
||||
),
|
||||
isUnder100kMarketCap = false,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -17,7 +17,7 @@ data class MarketsListItemUM(
|
|||
val trendPercentText: String,
|
||||
val trendType: PriceChangeType,
|
||||
val chardData: MarketChartRawData?,
|
||||
val isUnder100kMarketCap: Boolean = false,
|
||||
val isUnder100kMarketCap: Boolean,
|
||||
) {
|
||||
val chartType: MarketChartLook.Type = when (trendType) {
|
||||
PriceChangeType.UP -> MarketChartLook.Type.Growing
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ sealed class ListUM {
|
|||
|
||||
data class Content(
|
||||
val items: ImmutableList<MarketsListItemUM>,
|
||||
val showUnder100kTokens: Boolean,
|
||||
val showUnder100kTokensNotification: Boolean,
|
||||
val showUnder100kTokensNotificationWasHidden: Boolean,
|
||||
val loadMore: () -> Unit,
|
||||
val visibleIdsChanged: (List<String>) -> Unit,
|
||||
val onShowTokensUnder100kClicked: () -> Unit,
|
||||
|
|
|
|||
|
|
@ -462,7 +462,9 @@ private inline fun BaseScaffoldWithMarkets(
|
|||
color = BottomSheetDefaults.ScrimColor,
|
||||
visible = bottomSheetState.targetValue == TangemSheetValue.Expanded ||
|
||||
state.showMarketsOnboarding,
|
||||
onDismissRequest = { coroutineScope.launch { bottomSheetState.partialExpand() } },
|
||||
onDismissRequest = {
|
||||
coroutineScope.launch { bottomSheetState.partialExpand() }
|
||||
},
|
||||
)
|
||||
|
||||
MarketsTooltip(
|
||||
|
|
@ -486,7 +488,6 @@ private inline fun BaseScaffoldWithMarkets(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun MarketsTooltip(
|
||||
availableHeight: Dp,
|
||||
|
|
@ -644,6 +645,21 @@ private fun BottomSheetStateEffects(
|
|||
val systemUiController = rememberSystemUiController()
|
||||
val navigationBarColor = TangemTheme.colors.background.primary
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
delay(timeMillis = 100)
|
||||
when (bottomSheetState.currentValue) {
|
||||
TangemSheetValue.Hidden,
|
||||
TangemSheetValue.Expanded,
|
||||
-> systemUiController.setNavigationBarColor(
|
||||
color = Color.Transparent,
|
||||
darkIcons = navigationBarColor.luminance() > 0.5f,
|
||||
navigationBarContrastEnforced = true,
|
||||
)
|
||||
TangemSheetValue.PartiallyExpanded,
|
||||
-> systemUiController.setNavigationBarColor(navigationBarColor)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = bottomSheetState.targetValue, navigationBarColor) {
|
||||
when (bottomSheetState.targetValue) {
|
||||
TangemSheetValue.Hidden,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue