Updated on 2026-08-14
This commit is contained in:
parent
902d80d1f1
commit
e6cb71265e
7 changed files with 155 additions and 45 deletions
|
|
@ -7,27 +7,29 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
/**
|
||||
* Token list UM
|
||||
*
|
||||
* @property items items (search bar, tokens, headers)
|
||||
* @property isBalanceHidden flag that indicates if balance should be hidden
|
||||
* @property availableItems available items (search bar, header, tokens)
|
||||
* @property unavailableItems unavailable items (header, tokens)
|
||||
* @property isBalanceHidden flag that indicates if balance should be hidden
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal data class TokenListUM(
|
||||
val items: ImmutableList<TokensListItemUM>,
|
||||
val availableItems: ImmutableList<TokensListItemUM>,
|
||||
val unavailableItems: ImmutableList<TokensListItemUM>,
|
||||
val isBalanceHidden: Boolean,
|
||||
) {
|
||||
|
||||
/** Get search bar if it exists */
|
||||
fun getSearchBar(): TokensListItemUM.SearchBar? {
|
||||
return items.firstOrNull() as? TokensListItemUM.SearchBar
|
||||
return availableItems.firstOrNull() as? TokensListItemUM.SearchBar
|
||||
}
|
||||
|
||||
/** Get tokens */
|
||||
fun getTokens(): ImmutableList<TokensListItemUM> {
|
||||
if (getSearchBar() == null) return items
|
||||
if (getSearchBar() == null) return availableItems
|
||||
|
||||
return if (items.size > 1) {
|
||||
items.subList(fromIndex = 1, toIndex = items.size)
|
||||
return if (availableItems.size > 1) {
|
||||
availableItems.subList(fromIndex = 1, toIndex = availableItems.size)
|
||||
} else {
|
||||
persistentListOf()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ internal class TokenListUMController @Inject constructor() {
|
|||
|
||||
private val _state: MutableStateFlow<TokenListUM> = MutableStateFlow(
|
||||
value = TokenListUM(
|
||||
items = persistentListOf(),
|
||||
availableItems = persistentListOf(),
|
||||
unavailableItems = persistentListOf(),
|
||||
isBalanceHidden = false,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ internal abstract class SearchBarUMTransformer : TokenListUMTransformer {
|
|||
val updatedSearchBar = searchBarItem.copy(searchBarUM = transform(searchBarItem.searchBarUM))
|
||||
|
||||
prevState.copy(
|
||||
items = persistentListOf(
|
||||
availableItems = persistentListOf(
|
||||
updatedSearchBar,
|
||||
*prevState.getTokens().toTypedArray(),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,18 +1,26 @@
|
|||
package com.tangem.features.onramp.tokenlist.entity.transformer
|
||||
|
||||
import com.tangem.common.ui.tokens.TokenItemStateConverter
|
||||
import com.tangem.common.ui.tokens.TokenItemStateConverter.Companion.getFormattedFiatAmount
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.components.fields.entity.SearchBarUM
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.onramp.impl.R
|
||||
import com.tangem.features.onramp.tokenlist.entity.TokenListUM
|
||||
import com.tangem.features.onramp.tokenlist.entity.TokenListUMTransformer
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class UpdateTokenItemsTransformer(
|
||||
private val tokenItemStateConverter: TokenItemStateConverter,
|
||||
private val statuses: List<CryptoCurrencyStatus>,
|
||||
private val appCurrency: AppCurrency,
|
||||
private val onItemClick: (CryptoCurrencyStatus) -> Unit,
|
||||
private val statuses: Map<Boolean, List<CryptoCurrencyStatus>>,
|
||||
private val isBalanceHidden: Boolean,
|
||||
private val hasSearchBar: Boolean,
|
||||
private val onQueryChange: (String) -> Unit,
|
||||
|
|
@ -20,7 +28,15 @@ internal class UpdateTokenItemsTransformer(
|
|||
) : TokenListUMTransformer {
|
||||
|
||||
override fun transform(prevState: TokenListUM): TokenListUM {
|
||||
val items = tokenItemStateConverter.convertList(input = statuses).map(TokensListItemUM::Token)
|
||||
val availableItems = convertStatuses(
|
||||
converter = createDefaultTokenItemStateConverter(),
|
||||
statuses = statuses[true].orEmpty(),
|
||||
)
|
||||
|
||||
val unavailableItems = convertStatuses(
|
||||
converter = createUnavailableTokenItemStateConverter(),
|
||||
statuses = statuses[false].orEmpty(),
|
||||
)
|
||||
|
||||
val searchBarItem = if (hasSearchBar) {
|
||||
prevState.getSearchBar() ?: createSearchBarItem()
|
||||
|
|
@ -29,11 +45,80 @@ internal class UpdateTokenItemsTransformer(
|
|||
}
|
||||
|
||||
return prevState.copy(
|
||||
items = (listOfNotNull(searchBarItem) + items).toImmutableList(),
|
||||
availableItems = buildList {
|
||||
if (searchBarItem != null) {
|
||||
add(searchBarItem)
|
||||
}
|
||||
|
||||
createGroupTitle(
|
||||
textReference = resourceReference(id = R.string.exchange_tokens_available_tokens_header),
|
||||
)
|
||||
.let(::add)
|
||||
|
||||
addAll(availableItems)
|
||||
}
|
||||
.toImmutableList(),
|
||||
unavailableItems = buildList {
|
||||
if (unavailableItems.isNotEmpty()) {
|
||||
// TODO: [REDACTED_JIRA]
|
||||
createGroupTitle(
|
||||
textReference = stringReference(value = "Unavailable tokens"),
|
||||
)
|
||||
.let(::add)
|
||||
}
|
||||
|
||||
addAll(unavailableItems)
|
||||
}.toImmutableList(),
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
)
|
||||
}
|
||||
|
||||
private fun convertStatuses(
|
||||
converter: TokenItemStateConverter,
|
||||
statuses: List<CryptoCurrencyStatus>,
|
||||
): List<TokensListItemUM.Token> {
|
||||
return converter.convertList(statuses)
|
||||
.map(TokensListItemUM::Token)
|
||||
}
|
||||
|
||||
private fun createDefaultTokenItemStateConverter(): TokenItemStateConverter {
|
||||
return TokenItemStateConverter(appCurrency = appCurrency, onItemClick = onItemClick)
|
||||
}
|
||||
|
||||
private fun createUnavailableTokenItemStateConverter(): TokenItemStateConverter {
|
||||
return TokenItemStateConverter(
|
||||
appCurrency = appCurrency,
|
||||
iconStateProvider = { CryptoCurrencyToIconStateConverter(isAvailable = false).convert(it) },
|
||||
titleStateProvider = { TokenItemState.TitleState.Content(text = it.currency.name, isAvailable = false) },
|
||||
subtitleStateProvider = {
|
||||
when (it.value) {
|
||||
CryptoCurrencyStatus.Loading -> TokenItemState.SubtitleState.Loading
|
||||
else -> TokenItemState.SubtitleState.TextContent(value = it.currency.symbol, isAvailable = false)
|
||||
}
|
||||
},
|
||||
fiatAmountStateProvider = {
|
||||
when (it.value) {
|
||||
is CryptoCurrencyStatus.Loaded,
|
||||
is CryptoCurrencyStatus.Custom,
|
||||
is CryptoCurrencyStatus.NoQuote,
|
||||
is CryptoCurrencyStatus.NoAccount,
|
||||
-> {
|
||||
TokenItemState.FiatAmountState.TextContent(
|
||||
text = it.getFormattedFiatAmount(appCurrency),
|
||||
isAvailable = false,
|
||||
)
|
||||
}
|
||||
is CryptoCurrencyStatus.Unreachable,
|
||||
is CryptoCurrencyStatus.NoAmount,
|
||||
is CryptoCurrencyStatus.MissedDerivation,
|
||||
is CryptoCurrencyStatus.Loading,
|
||||
-> null
|
||||
}
|
||||
},
|
||||
onItemClick = onItemClick,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createSearchBarItem(): TokensListItemUM.SearchBar {
|
||||
return TokensListItemUM.SearchBar(
|
||||
searchBarUM = SearchBarUM(
|
||||
|
|
@ -45,4 +130,11 @@ internal class UpdateTokenItemsTransformer(
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createGroupTitle(textReference: TextReference): TokensListItemUM.NetworkGroupTitle {
|
||||
return TokensListItemUM.NetworkGroupTitle(
|
||||
id = textReference.hashCode(),
|
||||
name = textReference,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.features.onramp.tokenlist.model
|
||||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.ui.tokens.TokenItemStateConverter
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
|
|
@ -68,10 +67,8 @@ internal class OnrampTokenListModel @Inject constructor(
|
|||
.filterByAvailability()
|
||||
|
||||
UpdateTokenItemsTransformer(
|
||||
tokenItemStateConverter = TokenItemStateConverter(
|
||||
appCurrency = appCurrency,
|
||||
onItemClick = params.onTokenClick,
|
||||
),
|
||||
appCurrency = appCurrency,
|
||||
onItemClick = params.onTokenClick,
|
||||
statuses = filterTokenList,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
hasSearchBar = params.hasSearchBar && currencies.isNotEmpty(),
|
||||
|
|
@ -106,14 +103,18 @@ internal class OnrampTokenListModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun List<CryptoCurrencyStatus>.filterByAvailability(): List<CryptoCurrencyStatus> {
|
||||
return filter {
|
||||
when (params.filterOperation) {
|
||||
private fun List<CryptoCurrencyStatus>.filterByAvailability(): Map<Boolean, List<CryptoCurrencyStatus>> {
|
||||
return groupBy { status ->
|
||||
val isAvailable = when (params.filterOperation) {
|
||||
OnrampOperation.BUY -> {
|
||||
rampStateManager.availableForBuy(scanResponse = scanResponse, cryptoCurrency = it.currency)
|
||||
rampStateManager.availableForBuy(scanResponse = scanResponse, cryptoCurrency = status.currency)
|
||||
}
|
||||
OnrampOperation.SELL -> rampStateManager.availableForSell(cryptoCurrency = it.currency)
|
||||
OnrampOperation.SELL -> rampStateManager.availableForSell(cryptoCurrency = status.currency)
|
||||
}
|
||||
|
||||
isAvailable &&
|
||||
status.value !is CryptoCurrencyStatus.MissedDerivation &&
|
||||
status.value !is CryptoCurrencyStatus.Unreachable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,52 +4,65 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.SpacerH12
|
||||
import com.tangem.core.ui.components.tokenlist.TokenListItem
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.onramp.tokenlist.entity.TokenListUM
|
||||
import com.tangem.features.onramp.tokenlist.ui.preview.PreviewTokenListUMProvider
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
/**
|
||||
* Token list
|
||||
*
|
||||
* @param state state
|
||||
* @param modifier modifier
|
||||
* @param state state
|
||||
* @param contentPadding content padding
|
||||
* @param modifier modifier
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Composable
|
||||
internal fun TokenList(state: TokenListUM, contentPadding: PaddingValues, modifier: Modifier = Modifier) {
|
||||
LazyColumn(modifier = modifier, contentPadding = contentPadding) {
|
||||
itemsIndexed(
|
||||
items = state.items,
|
||||
key = { _, item -> item.id },
|
||||
contentType = { _, item -> item::class.java },
|
||||
itemContent = { index, item ->
|
||||
TokenListItem(
|
||||
state = item,
|
||||
isBalanceHidden = state.isBalanceHidden,
|
||||
modifier = Modifier
|
||||
.animateItem()
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = state.items.lastIndex,
|
||||
addDefaultPadding = false,
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
itemsBlock(items = state.availableItems, isBalanceHidden = state.isBalanceHidden)
|
||||
|
||||
item { SpacerH12() }
|
||||
|
||||
itemsBlock(items = state.unavailableItems, isBalanceHidden = state.isBalanceHidden)
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.itemsBlock(items: ImmutableList<TokensListItemUM>, isBalanceHidden: Boolean) {
|
||||
return itemsIndexed(
|
||||
items = items,
|
||||
key = { _, item -> item.id },
|
||||
contentType = { _, item -> item::class.java },
|
||||
itemContent = { index, item ->
|
||||
TokenListItem(
|
||||
state = item,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = Modifier
|
||||
.animateItem()
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = items.lastIndex,
|
||||
addDefaultPadding = false,
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun Preview_TokenList(@PreviewParameter(PreviewTokenListUMProvider::class) state: TokenListUM) {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ internal class PreviewTokenListUMProvider : PreviewParameterProvider<TokenListUM
|
|||
|
||||
private fun createTokensList(hasSearchBar: Boolean, vararg items: TokensListItemUM): TokenListUM {
|
||||
return TokenListUM(
|
||||
items = buildList {
|
||||
availableItems = buildList {
|
||||
if (hasSearchBar) {
|
||||
TokensListItemUM.SearchBar(
|
||||
searchBarUM = SearchBarUM(
|
||||
|
|
@ -37,6 +37,7 @@ internal class PreviewTokenListUMProvider : PreviewParameterProvider<TokenListUM
|
|||
addAll(items)
|
||||
}
|
||||
.toImmutableList(),
|
||||
unavailableItems = items.toList().toImmutableList(),
|
||||
isBalanceHidden = false,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue