Updated on 2026-08-14
This commit is contained in:
parent
af657b67fc
commit
9d5a56d97a
12 changed files with 201 additions and 101 deletions
|
|
@ -16,8 +16,8 @@ interface TangemTechMarketsApi {
|
|||
@Query("offset") offset: Int,
|
||||
@Query("limit") limit: Int,
|
||||
@Query("order") order: String,
|
||||
@Query("general_coins") generalCoins: Boolean,
|
||||
@Query("search") search: String?,
|
||||
@Query("timestamp") timestamp: Long?,
|
||||
): ApiResponse<TokenMarketListResponse>
|
||||
|
||||
@GET("coins/{coin_id}")
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ data class TokenMarketListResponse(
|
|||
val limit: Int,
|
||||
@Json(name = "offset")
|
||||
val offset: Int,
|
||||
@Json(name = "timestamp")
|
||||
val timestamp: Long? = null,
|
||||
) {
|
||||
data class Token(
|
||||
@Json(name = "id")
|
||||
|
|
|
|||
|
|
@ -114,6 +114,12 @@ private class DefaultBatchListSource<TKey, TData, TRequestParams : Any, TUpdate>
|
|||
loadMoreActionJob?.cancel()
|
||||
reloadActionJob?.cancel()
|
||||
stopAllUpdates()
|
||||
|
||||
state.value = BatchListState(
|
||||
data = emptyList(),
|
||||
status = PaginationStatus.InitialLoading,
|
||||
)
|
||||
|
||||
reloadActionJob = scope.launchFetch {
|
||||
reloadTask(action)
|
||||
}
|
||||
|
|
@ -221,11 +227,6 @@ private class DefaultBatchListSource<TKey, TData, TRequestParams : Any, TUpdate>
|
|||
}
|
||||
|
||||
private suspend fun reloadTask(action: BatchAction.Reload<TRequestParams>) {
|
||||
state.value = BatchListState(
|
||||
data = emptyList(),
|
||||
status = PaginationStatus.InitialLoading,
|
||||
)
|
||||
|
||||
val res = runCatching {
|
||||
batchFetcher.fetchFirst(action.requestParams)
|
||||
}.getOrElse {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
|||
import com.tangem.pagination.*
|
||||
import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
||||
internal class DefaultMarketsTokenRepository(
|
||||
|
|
@ -42,9 +43,9 @@ internal class DefaultMarketsTokenRepository(
|
|||
interval = request.params.priceChangeInterval.toRequestParam(),
|
||||
order = request.params.order.toRequestParam(),
|
||||
search = searchText,
|
||||
generalCoins = request.params.showUnder100kMarketCapTokens.not(),
|
||||
offset = request.offset,
|
||||
limit = request.limit,
|
||||
timestamp = if (isFirstBatchFetching) null else requestTimeStamp.get(),
|
||||
).getOrThrow()
|
||||
}
|
||||
|
||||
|
|
@ -58,7 +59,7 @@ internal class DefaultMarketsTokenRepository(
|
|||
}
|
||||
|
||||
if (isFirstBatchFetching) {
|
||||
requestTimeStamp.set(0) // TODO when backend is ready
|
||||
requestTimeStamp.set(res.timestamp ?: 0)
|
||||
}
|
||||
|
||||
val last = res.tokens.size < request.limit
|
||||
|
|
@ -82,10 +83,12 @@ internal class DefaultMarketsTokenRepository(
|
|||
marketsApi = marketsApi,
|
||||
)
|
||||
|
||||
val atomicInteger = AtomicInteger(0)
|
||||
|
||||
return BatchListSource(
|
||||
fetchDispatcher = dispatcherProvider.io,
|
||||
context = batchingContext,
|
||||
generateNewKey = { it.size },
|
||||
generateNewKey = { atomicInteger.getAndIncrement() },
|
||||
batchFetcher = createTokenMarketsFetcher(firstBatchSize = firstBatchSize, nextBatchSize = nextBatchSize),
|
||||
updateFetcher = tokenMarketsUpdateFetcher,
|
||||
).toBatchFlow()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.tangem.domain.markets
|
|||
data class TokenMarketListConfig(
|
||||
val fiatPriceCurrency: String,
|
||||
val searchText: String?,
|
||||
val showUnder100kMarketCapTokens: Boolean,
|
||||
val priceChangeInterval: Interval,
|
||||
val order: Order,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
package com.tangem.features.markets.tokenlist.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.lifecycle.compose.LifecycleStartEffect
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
|
|
@ -40,6 +38,13 @@ class DefaultMarketsTokenListComponent @AssistedInject constructor(
|
|||
onHeaderSizeChange: (Dp) -> Unit,
|
||||
modifier: Modifier,
|
||||
) {
|
||||
LifecycleStartEffect(Unit) {
|
||||
model.isVisibleOnScreen.value = true
|
||||
onStopOrDispose {
|
||||
model.isVisibleOnScreen.value = false
|
||||
}
|
||||
}
|
||||
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
val bsState by bottomSheetState
|
||||
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ internal class MarketsListModel @Inject constructor(
|
|||
val tokenSelected = _tokenSelected.asSharedFlow()
|
||||
|
||||
val containerBottomSheetState = MutableStateFlow(BottomSheetState.COLLAPSED)
|
||||
val isVisibleOnScreen = MutableStateFlow(false)
|
||||
|
||||
val state = marketsListUMStateManager.state.asStateFlow()
|
||||
|
||||
|
|
@ -201,6 +202,7 @@ internal class MarketsListModel @Inject constructor(
|
|||
marketsListUMStateManager.searchQueryFlow
|
||||
.filter { it.isNotEmpty() }
|
||||
.debounce(timeoutMillis = SEARCH_QUERY_DEBOUNCE_MILLIS)
|
||||
.distinctUntilChanged()
|
||||
.filter { activeListManager == searchMarketsListManager }
|
||||
.collectLatest {
|
||||
searchMarketsListManager.reload(searchText = it)
|
||||
|
|
@ -233,7 +235,10 @@ internal class MarketsListModel @Inject constructor(
|
|||
delay(timeMillis)
|
||||
// Update quotes only when the container bottom sheet is in the expanded state
|
||||
containerBottomSheetState.first { it == BottomSheetState.EXPANDED }
|
||||
activeListManager.updateQuotes() // TODO update a batch that is currently on screen
|
||||
// and is visible on the screen
|
||||
isVisibleOnScreen.first { it }
|
||||
|
||||
activeListManager.updateQuotes()
|
||||
}
|
||||
}.saveIn(updateQuotesJob)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ internal class MarketsTokenItemConverter(
|
|||
trendPercentText = value.getTrendPercent(),
|
||||
trendType = value.getTrendType(),
|
||||
chardData = value.getChartData(),
|
||||
showUnder100kMarketCap = value.isUnder100kMarketCap(),
|
||||
isUnder100kMarketCap = value.isUnder100kMarketCap(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +147,7 @@ internal class MarketsTokenItemConverter(
|
|||
}
|
||||
|
||||
private fun TokenMarket.isUnder100kMarketCap(): Boolean {
|
||||
return tokenQuotes.currentPrice.compareTo(decimal100k) == -1
|
||||
return marketCap?.let { it < decimal100k } ?: true
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
|
|
|||
|
|
@ -9,15 +9,19 @@ import com.tangem.features.markets.tokenlist.impl.model.utils.logUpdateResults
|
|||
import com.tangem.features.markets.tokenlist.impl.ui.entity.MarketsListItemUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.MarketsListUM.TrendInterval
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.SortByTypeUM
|
||||
import com.tangem.pagination.*
|
||||
import com.tangem.pagination.Batch
|
||||
import com.tangem.pagination.BatchAction
|
||||
import com.tangem.pagination.BatchFetchResult
|
||||
import com.tangem.pagination.PaginationStatus
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
private const val LOG_EVENTS = true
|
||||
|
||||
|
|
@ -33,6 +37,7 @@ internal class MarketsListBatchFlowManager(
|
|||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
private val actionsFlow = MutableSharedFlow<BatchAction<Int, TokenMarketListConfig, TokenMarketUpdateRequest>>()
|
||||
private val updateStateJob = JobHolder()
|
||||
|
||||
private val batchFlow = getMarketsTokenListFlowUseCase(
|
||||
batchingContext = TokenListBatchingContext(
|
||||
|
|
@ -42,6 +47,9 @@ internal class MarketsListBatchFlowManager(
|
|||
batchFlowType = batchFlowType,
|
||||
)
|
||||
|
||||
private val resultBatches = MutableStateFlow(ResultBatches())
|
||||
private val uiBatches = resultBatches.map { it.uiBatches }
|
||||
|
||||
val uiItems: StateFlow<ImmutableList<MarketsListItemUM>>
|
||||
get() = uiBatches
|
||||
.map { batches ->
|
||||
|
|
@ -97,16 +105,20 @@ internal class MarketsListBatchFlowManager(
|
|||
initialValue = false,
|
||||
)
|
||||
|
||||
private val uiBatches = MutableStateFlow<List<Batch<Int, List<MarketsListItemUM>>>>(emptyList())
|
||||
|
||||
init {
|
||||
batchFlow.state
|
||||
.map { it.data }
|
||||
.distinctUntilChanged { a, b ->
|
||||
a.size == b.size && a.map { it.data }.flatten() == b.map { it.data }.flatten()
|
||||
a.size == b.size &&
|
||||
a.map { it.key } == b.map { it.key } &&
|
||||
a.map { it.data }.flatten() == b.map { it.data }.flatten()
|
||||
}
|
||||
.onEachWithPrevious { prev, list ->
|
||||
updateState(prev, list)
|
||||
.onEach {
|
||||
coroutineScope {
|
||||
launch {
|
||||
updateState(it)
|
||||
}.saveIn(updateStateJob)
|
||||
}
|
||||
}
|
||||
.flowOn(dispatchers.default)
|
||||
.launchIn(modelScope)
|
||||
|
|
@ -127,58 +139,75 @@ internal class MarketsListBatchFlowManager(
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateState(
|
||||
previousList: List<Batch<Int, List<TokenMarket>>>?,
|
||||
list: List<Batch<Int, List<TokenMarket>>>,
|
||||
forceUpdate: Boolean = false,
|
||||
) = uiBatches.update { items ->
|
||||
val converter = MarketsTokenItemConverter(currentTrendInterval(), appCurrency = currentAppCurrency())
|
||||
private suspend fun updateState(newList: List<Batch<Int, List<TokenMarket>>>, forceUpdate: Boolean = false) =
|
||||
withContext(dispatchers.default) {
|
||||
resultBatches.update { resultBatches ->
|
||||
val items = resultBatches.uiBatches
|
||||
val previousList = resultBatches.processedItems
|
||||
|
||||
if (previousList == null || list.size < previousList.size || forceUpdate) {
|
||||
list.map {
|
||||
Batch(
|
||||
key = it.key,
|
||||
data = converter.convertList(it.data),
|
||||
val converter = MarketsTokenItemConverter(currentTrendInterval(), appCurrency = currentAppCurrency())
|
||||
|
||||
if (newList.isEmpty()) {
|
||||
return@update ResultBatches(processedItems = emptyList())
|
||||
}
|
||||
|
||||
val isInitialLoading =
|
||||
forceUpdate || previousList.isNullOrEmpty() || newList.first().key != previousList.first().key
|
||||
|
||||
val outItems = if (isInitialLoading) {
|
||||
newList.map {
|
||||
Batch(
|
||||
key = it.key,
|
||||
data = converter.convertList(it.data),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
previousList!!
|
||||
if (previousList.size != newList.size) {
|
||||
val keysToAdd = newList.map { it.key }.subtract(previousList.map { it.key }.toSet())
|
||||
val newBatches = newList.filter { keysToAdd.contains(it.key) }
|
||||
|
||||
items + newBatches.map {
|
||||
Batch(
|
||||
key = it.key,
|
||||
data = converter.convertList(it.data),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
items.mapIndexed { batchIndex, batch ->
|
||||
val prevBatch = previousList[batchIndex]
|
||||
val newBatch = newList[batchIndex]
|
||||
if (previousList == newBatch) return@mapIndexed batch
|
||||
|
||||
Batch(
|
||||
key = batch.key,
|
||||
data = batch.data.mapIndexed { index, marketsListItemUM ->
|
||||
val prevItem = prevBatch.data[index]
|
||||
val newItem = newBatch.data[index]
|
||||
|
||||
converter.update(
|
||||
prevItem,
|
||||
marketsListItemUM,
|
||||
newItem,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentCoroutineContext().ensureActive()
|
||||
|
||||
ResultBatches(
|
||||
uiBatches = outItems,
|
||||
processedItems = newList,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (previousList.size != list.size) {
|
||||
val keysToAdd = list.map { it.key }.subtract(previousList.map { it.key }.toSet())
|
||||
val newBatches = list.filter { keysToAdd.contains(it.key) }
|
||||
|
||||
items + newBatches.map {
|
||||
Batch(
|
||||
key = it.key,
|
||||
data = converter.convertList(it.data),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
items.mapIndexed { batchIndex, batch ->
|
||||
val prevBatch = previousList[batchIndex]
|
||||
val newBatch = list[batchIndex]
|
||||
if (previousList == newBatch) return@mapIndexed batch
|
||||
|
||||
Batch(
|
||||
key = batch.key,
|
||||
data = batch.data.mapIndexed { index, marketsListItemUM ->
|
||||
val prevItem = prevBatch.data[index]
|
||||
val newItem = newBatch.data[index]
|
||||
|
||||
converter.update(
|
||||
prevItem,
|
||||
marketsListItemUM,
|
||||
newItem,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun reload(searchText: String? = null) {
|
||||
modelScope.launch {
|
||||
uiBatches.value = emptyList()
|
||||
resultBatches.value = ResultBatches()
|
||||
actionsFlow.emit(
|
||||
BatchAction.Reload(
|
||||
requestParams = TokenMarketListConfig(
|
||||
|
|
@ -188,7 +217,6 @@ internal class MarketsListBatchFlowManager(
|
|||
} else {
|
||||
searchText ?: currentSearchText()
|
||||
},
|
||||
showUnder100kMarketCapTokens = false, // TODO
|
||||
priceChangeInterval = currentTrendInterval().toBatchRequestInterval(),
|
||||
order = currentSortByType().toRequestOrder(),
|
||||
),
|
||||
|
|
@ -206,12 +234,14 @@ internal class MarketsListBatchFlowManager(
|
|||
fun updateUIWithSameState() {
|
||||
modelScope.launch(dispatchers.default) {
|
||||
val current = batchFlow.state.value.data
|
||||
updateState(current, current, forceUpdate = true)
|
||||
}
|
||||
updateState(current, forceUpdate = true)
|
||||
}.saveIn(updateStateJob)
|
||||
}
|
||||
|
||||
fun loadCharts(batchKeys: Set<Int>, interval: TrendInterval) {
|
||||
modelScope.launch(dispatchers.default) {
|
||||
if (batchKeys.isEmpty()) return
|
||||
|
||||
modelScope.launch {
|
||||
val currentData = batchFlow.state.value.data
|
||||
val alreadyLoadedChartsBatchKeys = currentData
|
||||
.filter {
|
||||
|
|
@ -266,7 +296,7 @@ internal class MarketsListBatchFlowManager(
|
|||
}
|
||||
|
||||
fun clearStateAndStopAllActions() {
|
||||
uiBatches.value = emptyList()
|
||||
resultBatches.value = ResultBatches()
|
||||
modelScope.launch {
|
||||
actionsFlow.emit(BatchAction.Reset)
|
||||
}
|
||||
|
|
@ -303,12 +333,8 @@ internal class MarketsListBatchFlowManager(
|
|||
}
|
||||
}
|
||||
|
||||
private fun <T> Flow<T>.onEachWithPrevious(operation: suspend (prev: T?, value: T) -> Unit): Flow<T> = flow {
|
||||
var prev: T? = null
|
||||
collect { value ->
|
||||
operation(prev, value)
|
||||
prev = value
|
||||
emit(value)
|
||||
}
|
||||
}
|
||||
private data class ResultBatches(
|
||||
val uiBatches: List<Batch<Int, List<MarketsListItemUM>>> = emptyList(),
|
||||
val processedItems: List<Batch<Int, List<TokenMarket>>>? = null,
|
||||
)
|
||||
}
|
||||
|
|
@ -13,6 +13,8 @@ import com.tangem.features.markets.tokenlist.impl.ui.entity.MarketsListItemUM
|
|||
import com.tangem.features.markets.tokenlist.impl.ui.entity.MarketsListUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.entity.SortByTypeUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
@Stable
|
||||
|
|
@ -99,22 +101,74 @@ internal class MarketsListUMStateManager(
|
|||
it.copy(list = ListUM.Loading)
|
||||
}
|
||||
else -> {
|
||||
it.copy(
|
||||
list = ListUM.Content(
|
||||
items = uiItems,
|
||||
loadMore = onLoadMoreUiItems,
|
||||
visibleIdsChanged = visibleItemsChanged,
|
||||
showUnder100kTokens = true,
|
||||
onShowTokensUnder100kClicked = { },
|
||||
triggerScrollReset = consumedEvent(),
|
||||
onItemClick = onTokenClick,
|
||||
),
|
||||
)
|
||||
it.updateItems(newItems = uiItems)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 (filtered.size == newItems.size) {
|
||||
return currentState.copy(list = generalContentState(newItems))
|
||||
} else {
|
||||
filtered
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
searchUiItemsCached = persistentListOf()
|
||||
newItems
|
||||
}
|
||||
}
|
||||
|
||||
return currentState.copy(
|
||||
list = ListUM.Content(
|
||||
items = items,
|
||||
loadMore = onLoadMoreUiItems,
|
||||
visibleIdsChanged = visibleItemsChanged,
|
||||
showUnder100kTokens = isInSearchState.not() || isNextPageInSearch,
|
||||
onShowTokensUnder100kClicked = {
|
||||
if (searchUiItemsCached.isNotEmpty()) {
|
||||
state.update { s ->
|
||||
if (s.list is ListUM.Content) {
|
||||
s.copy(
|
||||
list = s.list.copy(
|
||||
items = searchUiItemsCached,
|
||||
showUnder100kTokens = true,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
triggerScrollReset = consumedEvent(),
|
||||
onItemClick = onTokenClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun generalContentState(newItems: ImmutableList<MarketsListItemUM>): ListUM.Content {
|
||||
return ListUM.Content(
|
||||
items = newItems,
|
||||
loadMore = onLoadMoreUiItems,
|
||||
visibleIdsChanged = visibleItemsChanged,
|
||||
showUnder100kTokens = true,
|
||||
onShowTokensUnder100kClicked = {},
|
||||
triggerScrollReset = consumedEvent(),
|
||||
onItemClick = onTokenClick,
|
||||
)
|
||||
}
|
||||
|
||||
private fun state(): MarketsListUM = MarketsListUM(
|
||||
list = ListUM.Loading,
|
||||
searchBar = SearchBarUM(
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import com.tangem.features.markets.tokenlist.impl.ui.entity.ListUM
|
|||
import kotlinx.coroutines.launch
|
||||
|
||||
private const val LOAD_NEXT_PAGE_ON_END_INDEX = 50
|
||||
private const val TOKEN_LAZY_LIST_ID_SEPARATOR = "***"
|
||||
|
||||
@Composable
|
||||
@Suppress("LongMethod")
|
||||
|
|
@ -89,7 +90,7 @@ internal fun MarketsListLazyColumn(
|
|||
is ListUM.Content -> {
|
||||
items(
|
||||
items = state.items,
|
||||
key = { it.id + it.marketCap.toString() },
|
||||
key = { it.id + TOKEN_LAZY_LIST_ID_SEPARATOR + it.marketCap.toString() },
|
||||
) { item ->
|
||||
MarketsListItem(
|
||||
model = item,
|
||||
|
|
@ -117,8 +118,11 @@ internal fun MarketsListLazyColumn(
|
|||
buffer = LOAD_NEXT_PAGE_ON_END_INDEX,
|
||||
onLoadMore = remember(state) {
|
||||
{
|
||||
if (state is ListUM.Content) {
|
||||
if (state is ListUM.Content && state.showUnder100kTokens) {
|
||||
state.loadMore()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -184,7 +188,9 @@ private fun SearchNothingFoundText(modifier: Modifier = Modifier) {
|
|||
private fun VisibleItemsTracker(listState: LazyListState, state: ListUM) {
|
||||
val visibleItems by remember {
|
||||
derivedStateOf {
|
||||
listState.layoutInfo.visibleItemsInfo.mapNotNull { it.key as? String }
|
||||
listState.layoutInfo.visibleItemsInfo.mapNotNull {
|
||||
(it.key as? String)?.split(TOKEN_LAZY_LIST_ID_SEPARATOR)?.first()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -196,7 +202,7 @@ private fun VisibleItemsTracker(listState: LazyListState, state: ListUM) {
|
|||
}
|
||||
|
||||
@Composable
|
||||
fun InfiniteListHandler(listState: LazyListState, onLoadMore: () -> Unit, buffer: Int = 2) {
|
||||
fun InfiniteListHandler(listState: LazyListState, onLoadMore: () -> Boolean, buffer: Int = 2) {
|
||||
val loadMore by remember {
|
||||
derivedStateOf {
|
||||
val layoutInfo = listState.layoutInfo
|
||||
|
|
@ -212,8 +218,7 @@ fun InfiniteListHandler(listState: LazyListState, onLoadMore: () -> Unit, buffer
|
|||
|
||||
LaunchedEffect(loadMore) {
|
||||
if (loadMore && !emitted) {
|
||||
emitted = true
|
||||
onLoadMore()
|
||||
emitted = onLoadMore()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ data class MarketsListItemUM(
|
|||
val trendPercentText: String,
|
||||
val trendType: PriceChangeType,
|
||||
val chardData: MarketChartRawData?,
|
||||
val showUnder100kMarketCap: Boolean = false,
|
||||
val isUnder100kMarketCap: Boolean = false,
|
||||
) {
|
||||
val chartType: MarketChartLook.Type = when (trendType) {
|
||||
PriceChangeType.UP,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue