Updated on 2026-08-14
This commit is contained in:
commit
a651da10e5
143 changed files with 3655 additions and 899 deletions
|
|
@ -148,6 +148,13 @@ internal class FeedEntryChildFactory @Inject constructor(
|
|||
appComponentContext = appComponentContext,
|
||||
params = DefaultSearchComponent.Params(
|
||||
onBackClick = onBackClicked,
|
||||
onMarketTokenClick = { token, currency ->
|
||||
feedEntryClickIntents.onMarketItemClick(
|
||||
token = token,
|
||||
appCurrency = currency,
|
||||
source = AnalyticsParam.ScreensSources.Market.value,
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import com.tangem.core.ui.ds.field.search.TangemFieldShape
|
|||
import com.tangem.core.ui.ds.field.search.TangemSearchField
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBarType
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.TokenMarketParams
|
||||
import com.tangem.features.feed.model.search.SearchModel
|
||||
import com.tangem.features.feed.ui.search.SearchContent
|
||||
import com.tangem.features.feed.ui.search.state.SearchCallbacks
|
||||
|
|
@ -74,6 +76,7 @@ internal class DefaultSearchComponent(
|
|||
onClearHintsClick = model::clearSearchHistory,
|
||||
onTextHintClick = model::onTextHintClick,
|
||||
onResultMarketTokenClick = model::onResultMarketTokenClick,
|
||||
onHistoryTokenClick = model::onHistoryTokenClick,
|
||||
)
|
||||
}
|
||||
SearchContent(
|
||||
|
|
@ -86,5 +89,6 @@ internal class DefaultSearchComponent(
|
|||
|
||||
data class Params(
|
||||
val onBackClick: () -> Unit,
|
||||
val onMarketTokenClick: ((TokenMarketParams, AppCurrency) -> Unit),
|
||||
)
|
||||
}
|
||||
|
|
@ -1,19 +1,21 @@
|
|||
package com.tangem.features.feed.model.search
|
||||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.ui.charts.state.MarketChartData
|
||||
import com.tangem.common.ui.charts.state.converter.PriceAndTimePointValuesConverter
|
||||
import com.tangem.common.ui.charts.state.sorted
|
||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.common.ui.charts.state.MarketChartData
|
||||
import com.tangem.common.ui.charts.state.converter.PriceAndTimePointValuesConverter
|
||||
import com.tangem.common.ui.charts.state.sorted
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.markets.GetMarketsTokenListFlowUseCase
|
||||
import com.tangem.domain.markets.GetTokenPriceChartUseCase
|
||||
import com.tangem.domain.markets.PriceChangeInterval
|
||||
import com.tangem.domain.models.account.AccountName
|
||||
import com.tangem.domain.markets.TokenMarketParams
|
||||
import com.tangem.domain.markets.toSerializableParam
|
||||
import com.tangem.domain.search.usecase.ClearSearchHistoryUseCase
|
||||
import com.tangem.domain.search.usecase.GetSearchResultsUseCase
|
||||
import com.tangem.domain.search.usecase.SaveRecentSearchTokenUseCase
|
||||
|
|
@ -26,6 +28,7 @@ import com.tangem.features.feed.model.search.converter.MarketsListItemUMToRecent
|
|||
import com.tangem.features.feed.model.search.converter.MarketsListItemUMWithAppCurrency
|
||||
import com.tangem.features.feed.model.search.converter.RecentSearchTokenToMarketsListItemUMConverter
|
||||
import com.tangem.features.feed.model.search.converter.RecentSearchTokenWithAppCurrency
|
||||
import com.tangem.features.feed.model.search.converter.UserAssetSearchItemConverter
|
||||
import com.tangem.features.feed.model.search.state.SearchStateController
|
||||
import com.tangem.features.feed.model.search.state.transformers.*
|
||||
import com.tangem.features.feed.ui.search.state.*
|
||||
|
|
@ -35,13 +38,8 @@ import com.tangem.utils.coroutines.JobHolder
|
|||
import com.tangem.utils.coroutines.saveIn
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val UPDATE_QUOTES_TIMER_MILLIS = 60000L
|
||||
|
|
@ -54,6 +52,7 @@ internal class SearchModel @Inject constructor(
|
|||
paramsContainer: ParamsContainer,
|
||||
getMarketsTokenListFlowUseCase: GetMarketsTokenListFlowUseCase,
|
||||
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
private val getSearchResultsUseCase: GetSearchResultsUseCase,
|
||||
private val saveSearchQueryUseCase: SaveSearchQueryUseCase,
|
||||
private val saveRecentSearchTokenUseCase: SaveRecentSearchTokenUseCase,
|
||||
|
|
@ -77,6 +76,13 @@ internal class SearchModel @Inject constructor(
|
|||
initialValue = AppCurrency.Default,
|
||||
)
|
||||
|
||||
private val isBalanceHidden = getBalanceHidingSettingsUseCase.isBalanceHidden()
|
||||
.stateIn(
|
||||
scope = modelScope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = false,
|
||||
)
|
||||
|
||||
private val marketsListItemToRecentSearchTokenConverter by lazy {
|
||||
MarketsListItemUMToRecentSearchTokenConverter()
|
||||
}
|
||||
|
|
@ -143,17 +149,38 @@ internal class SearchModel @Inject constructor(
|
|||
)
|
||||
saveRecentSearchTokenUseCase(marketsListItemToRecentSearchTokenConverter.convert(input))
|
||||
saveSearchQueryUseCase(stateController.value.searchBar.query)
|
||||
withContext(dispatchers.mainImmediate) {
|
||||
searchMarketsListManager.getTokenById(item.id)?.let { found ->
|
||||
params.onMarketTokenClick(found.toSerializableParam(), appCurrency)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onHistoryTokenClick(item: MarketsListItemUM) {
|
||||
val tokenMarketParams = TokenMarketParams(
|
||||
id = item.id,
|
||||
name = item.name,
|
||||
symbol = item.currencySymbol,
|
||||
tokenQuotes = TokenMarketParams.Quotes(
|
||||
currentPrice = item.price.fiatPrice,
|
||||
h24Percent = null,
|
||||
weekPercent = null,
|
||||
monthPercent = null,
|
||||
),
|
||||
imageUrl = item.iconUrl,
|
||||
)
|
||||
params.onMarketTokenClick(tokenMarketParams, currentAppCurrency.value)
|
||||
}
|
||||
|
||||
private fun initCallbacks() {
|
||||
stateController.update(object : SearchUMTransformer {
|
||||
override fun transform(prevState: SearchUM): SearchUM {
|
||||
return prevState.copy(
|
||||
searchBar = prevState.searchBar.copy(
|
||||
onQueryChange = ::onQueryChange,
|
||||
onActiveChange = ::onActiveChange,
|
||||
onClearClick = ::onClearClick,
|
||||
onCancelClick = params.onBackClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -164,10 +191,6 @@ internal class SearchModel @Inject constructor(
|
|||
stateController.update(UpdateSearchBarQueryTransformer(query))
|
||||
}
|
||||
|
||||
private fun onActiveChange(isActive: Boolean) {
|
||||
if (!isActive) params.onBackClick()
|
||||
}
|
||||
|
||||
private fun onClearClick() {
|
||||
stateController.update(UpdateSearchBarQueryTransformer(""))
|
||||
}
|
||||
|
|
@ -200,20 +223,19 @@ internal class SearchModel @Inject constructor(
|
|||
|
||||
private fun subscribeToSearchResults(query: String) {
|
||||
modelScope.launch {
|
||||
getSearchResultsUseCase(query = query).collectLatest { searchResult ->
|
||||
val userAssets = searchResult.userAssets.map { entry ->
|
||||
UserAssetItemUM(
|
||||
id = "${entry.userWalletId.stringValue}_${entry.accountId.value}" +
|
||||
"_${entry.currencyStatus.currency.id.value}",
|
||||
tokenIconUrl = entry.currencyStatus.currency.iconUrl,
|
||||
tokenName = entry.currencyStatus.currency.name,
|
||||
tokenSymbol = entry.currencyStatus.currency.symbol,
|
||||
accountName = entry.accountName.toDisplayString(),
|
||||
onClick = {
|
||||
// TODO in [REDACTED_TASK_KEY] while just a stub item. Will be handled in next task.
|
||||
},
|
||||
)
|
||||
}.toImmutableList()
|
||||
combine(
|
||||
getSearchResultsUseCase(query = query),
|
||||
currentAppCurrency,
|
||||
isBalanceHidden,
|
||||
) { searchResult, appCurrency, balanceHidden ->
|
||||
val converter = UserAssetSearchItemConverter(
|
||||
appCurrency = appCurrency,
|
||||
isBalanceHidden = balanceHidden,
|
||||
)
|
||||
searchResult.userAssets
|
||||
.map(converter::convert)
|
||||
.toImmutableList()
|
||||
}.collectLatest { userAssets ->
|
||||
stateController.update(UpdateUserAssetsTransformer(userAssets))
|
||||
}
|
||||
}.saveIn(searchResultsJob)
|
||||
|
|
@ -334,13 +356,6 @@ internal class SearchModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun AccountName.toDisplayString(): String {
|
||||
return when (this) {
|
||||
is AccountName.DefaultMain -> "Main" // TODO [REDACTED_TASK_KEY] localize
|
||||
is AccountName.Custom -> value
|
||||
}
|
||||
}
|
||||
|
||||
private fun CoroutineScope.loadQuotesWithTimer(timeMillis: Long) {
|
||||
launch {
|
||||
while (true) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
package com.tangem.features.feed.model.search.converter
|
||||
|
||||
import com.tangem.common.ui.account.toUM
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.search.model.UserAssetSearchEntry
|
||||
import com.tangem.domain.search.model.UserAssetSearchItem
|
||||
import com.tangem.features.feed.ui.search.state.UserAssetItemUM
|
||||
import com.tangem.utils.StringsSigns
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class UserAssetSearchItemConverter(
|
||||
private val appCurrency: AppCurrency,
|
||||
private val isBalanceHidden: Boolean,
|
||||
) : Converter<UserAssetSearchItem, UserAssetItemUM> {
|
||||
|
||||
override fun convert(value: UserAssetSearchItem): UserAssetItemUM {
|
||||
return when (value) {
|
||||
is UserAssetSearchItem.Single -> convertSingle(value.entry)
|
||||
is UserAssetSearchItem.Grouped -> convertGrouped(value)
|
||||
}
|
||||
}
|
||||
|
||||
private fun convertSingle(entry: UserAssetSearchEntry): UserAssetItemUM.Single {
|
||||
val currency = entry.currencyStatus.currency
|
||||
val value = entry.currencyStatus.value
|
||||
|
||||
return UserAssetItemUM.Single(
|
||||
id = "${entry.userWalletId.stringValue}_${entry.accountId.value}_${currency.id.value}",
|
||||
icon = TangemIconUM.Currency(
|
||||
currencyIconState = CryptoCurrencyToIconStateConverter().convert(entry.currencyStatus),
|
||||
),
|
||||
tokenName = currency.name,
|
||||
tokenSymbol = currency.symbol,
|
||||
fiatRate = value.fiatRate?.format { fiat(appCurrency.code, appCurrency.symbol) },
|
||||
cryptoBalance = formatCryptoAmount(value.amount, currency.symbol, currency.decimals),
|
||||
fiatBalance = formatFiatAmount(value.fiatAmount),
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
|
||||
private fun convertGrouped(item: UserAssetSearchItem.Grouped): UserAssetItemUM.Grouped {
|
||||
val totalFiat = item.entries.sumOf { it.currencyStatus.value.fiatAmount ?: BigDecimal.ZERO }
|
||||
val totalCrypto = item.entries.sumOf { it.currencyStatus.value.amount ?: BigDecimal.ZERO }
|
||||
|
||||
val firstCurrency = item.entries.first().currencyStatus.currency
|
||||
val children = item.entries.map { entry ->
|
||||
UserAssetItemUM.GroupedChild(
|
||||
walletName = entry.userWalletName,
|
||||
accountName = entry.accountName.toUM(),
|
||||
accountIcon = entry.accountIcon.value,
|
||||
accountColor = entry.accountIcon.color,
|
||||
cryptoBalance = formatCryptoAmount(
|
||||
entry.currencyStatus.value.amount,
|
||||
entry.currencyStatus.currency.symbol,
|
||||
entry.currencyStatus.currency.decimals,
|
||||
),
|
||||
fiatBalance = formatFiatAmount(entry.currencyStatus.value.fiatAmount),
|
||||
)
|
||||
}.toImmutableList()
|
||||
|
||||
return UserAssetItemUM.Grouped(
|
||||
id = "grouped_${item.tokenName}_${item.tokenSymbol}",
|
||||
icon = TangemIconUM.Currency(
|
||||
currencyIconState = CryptoCurrencyToIconStateConverter().convert(item.entries.first().currencyStatus),
|
||||
),
|
||||
tokenName = item.tokenName,
|
||||
tokenSymbol = item.tokenSymbol,
|
||||
tokensCount = item.entries.size,
|
||||
totalCryptoBalance = formatCryptoAmount(totalCrypto, firstCurrency.symbol, firstCurrency.decimals),
|
||||
totalFiatBalance = formatFiatAmount(totalFiat),
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
children = children,
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
|
||||
private fun formatCryptoAmount(amount: BigDecimal?, symbol: String, decimals: Int): String {
|
||||
return amount?.format { crypto(symbol, decimals) } ?: StringsSigns.DASH_SIGN
|
||||
}
|
||||
|
||||
private fun formatFiatAmount(fiatAmount: BigDecimal?): String {
|
||||
return fiatAmount?.format { fiat(appCurrency.code, appCurrency.symbol) } ?: StringsSigns.DASH_SIGN
|
||||
}
|
||||
}
|
||||
|
|
@ -3,20 +3,16 @@ package com.tangem.features.feed.ui.search
|
|||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.lazy.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
|
|
@ -32,7 +28,6 @@ import com.tangem.core.ui.components.SpacerW
|
|||
import com.tangem.core.ui.components.list.InfiniteListHandler
|
||||
import com.tangem.core.ui.ds.button.*
|
||||
import com.tangem.core.ui.ds.image.TangemIcon
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.extensions.clickableSingle
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
|
|
@ -54,6 +49,15 @@ internal fun SearchContent(
|
|||
val lazyListState = rememberLazyListState()
|
||||
val background = LocalMainBottomSheetColor.current.value
|
||||
|
||||
val contentStructureKey = when (content) {
|
||||
is SearchContentUM.InitialEmpty -> "empty"
|
||||
is SearchContentUM.History -> "history"
|
||||
is SearchContentUM.Results -> "results_${content.userAssets.isNotEmpty()}"
|
||||
}
|
||||
LaunchedEffect(contentStructureKey) {
|
||||
lazyListState.scrollToItem(0)
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
state = lazyListState,
|
||||
modifier = modifier
|
||||
|
|
@ -72,6 +76,7 @@ internal fun SearchContent(
|
|||
history = content,
|
||||
onClearAllClick = searchCallbacks.onClearHintsClick,
|
||||
onHintClick = searchCallbacks.onTextHintClick,
|
||||
onHistoryTokenClick = searchCallbacks.onHistoryTokenClick,
|
||||
)
|
||||
is SearchContentUM.Results -> searchResultsItems(
|
||||
results = content,
|
||||
|
|
@ -98,6 +103,7 @@ private fun LazyListScope.searchHistoryItems(
|
|||
history: SearchContentUM.History,
|
||||
onClearAllClick: (() -> Unit),
|
||||
onHintClick: (String) -> Unit,
|
||||
onHistoryTokenClick: (MarketsListItemUM) -> Unit,
|
||||
) {
|
||||
if (!history.textHints.isEmpty() || !history.recentTokens.isEmpty()) {
|
||||
item(key = "recents") {
|
||||
|
|
@ -107,15 +113,20 @@ private fun LazyListScope.searchHistoryItems(
|
|||
)
|
||||
}
|
||||
}
|
||||
items(
|
||||
itemsIndexed(
|
||||
items = history.textHints,
|
||||
key = { "hint_${it.text}" },
|
||||
) { hint ->
|
||||
key = { _, item -> "hint_${item.text}" },
|
||||
) { index, hint ->
|
||||
TextHintItem(hint = hint, onHintClick = { onHintClick(hint.text) })
|
||||
HorizontalDivider(
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens2.x2),
|
||||
color = TangemTheme.colors2.border.neutral.primary,
|
||||
)
|
||||
if (index < history.textHints.size - 1) {
|
||||
HorizontalDivider(
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens2.x2),
|
||||
color = TangemTheme.colors2.border.neutral.primary,
|
||||
)
|
||||
}
|
||||
}
|
||||
item {
|
||||
SpacerH(TangemTheme.dimens2.x2)
|
||||
}
|
||||
items(
|
||||
items = history.recentTokens,
|
||||
|
|
@ -129,7 +140,7 @@ private fun LazyListScope.searchHistoryItems(
|
|||
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
|
||||
),
|
||||
model = token,
|
||||
onClick = {}, // TODO in [REDACTED_TASK_KEY]
|
||||
onClick = { onHistoryTokenClick(token) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -264,9 +275,16 @@ private fun TextHintItem(hint: TextHintItemUM, onHintClick: () -> Unit) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO in [REDACTED_TASK_KEY] while just a stub item. Will be handled in next task.
|
||||
@Composable
|
||||
private fun UserAssetItem(asset: UserAssetItemUM) {
|
||||
when (asset) {
|
||||
is UserAssetItemUM.Single -> SingleUserAssetItem(asset)
|
||||
is UserAssetItemUM.Grouped -> GroupedUserAssetItem(asset)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SingleUserAssetItem(asset: UserAssetItemUM.Single) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
|
@ -276,10 +294,8 @@ private fun UserAssetItem(asset: UserAssetItemUM) {
|
|||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
TangemIcon(
|
||||
tangemIconUM = TangemIconUM.Url(asset.tokenIconUrl, fallbackRes = R.drawable.ic_custom_token_44),
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.clip(CircleShape),
|
||||
modifier = Modifier.size(40.dp),
|
||||
tangemIconUM = asset.icon,
|
||||
)
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
|
|
@ -289,12 +305,79 @@ private fun UserAssetItem(asset: UserAssetItemUM) {
|
|||
maxLines = 1,
|
||||
)
|
||||
Text(
|
||||
text = "${asset.tokenSymbol} · ${asset.accountName}",
|
||||
text = asset.tokenSymbol,
|
||||
style = TangemTheme.typography2.captionRegular13,
|
||||
color = TangemTheme.colors2.text.neutral.tertiary,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
if (!asset.isBalanceHidden) {
|
||||
Column(horizontalAlignment = Alignment.End) {
|
||||
Text(
|
||||
text = asset.fiatBalance,
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
maxLines = 1,
|
||||
)
|
||||
Text(
|
||||
text = asset.cryptoBalance,
|
||||
style = TangemTheme.typography2.captionRegular13,
|
||||
color = TangemTheme.colors2.text.neutral.tertiary,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO [REDACTED_JIRA] update ui item to Portfolio block item
|
||||
@Composable
|
||||
private fun GroupedUserAssetItem(asset: UserAssetItemUM.Grouped) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = asset.onClick)
|
||||
.padding(horizontal = 12.dp, vertical = 14.dp),
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
TangemIcon(
|
||||
modifier = Modifier.size(40.dp),
|
||||
tangemIconUM = asset.icon,
|
||||
)
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = asset.tokenName,
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
maxLines = 1,
|
||||
)
|
||||
Text(
|
||||
text = "${asset.tokenSymbol} · ${asset.tokensCount}",
|
||||
style = TangemTheme.typography2.captionRegular13,
|
||||
color = TangemTheme.colors2.text.neutral.tertiary,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
if (!asset.isBalanceHidden) {
|
||||
Column(horizontalAlignment = Alignment.End) {
|
||||
Text(
|
||||
text = asset.totalFiatBalance,
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
maxLines = 1,
|
||||
)
|
||||
Text(
|
||||
text = asset.totalCryptoBalance,
|
||||
style = TangemTheme.typography2.captionRegular13,
|
||||
color = TangemTheme.colors2.text.neutral.tertiary,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,7 @@ package com.tangem.features.feed.ui.search.preview
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -15,7 +11,9 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
|||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.charts.state.MarketChartRawData
|
||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
|
|
@ -206,18 +204,22 @@ internal object SearchContentPreviewFixtures {
|
|||
updateTimestamp = updateTimestamp,
|
||||
)
|
||||
|
||||
private fun userAsset(
|
||||
id: String,
|
||||
name: String,
|
||||
symbol: String,
|
||||
accountName: String,
|
||||
iconUrl: String? = null,
|
||||
): UserAssetItemUM = UserAssetItemUM(
|
||||
private fun userAsset(id: String, name: String, symbol: String): UserAssetItemUM = UserAssetItemUM.Single(
|
||||
id = id,
|
||||
tokenIconUrl = iconUrl,
|
||||
icon = TangemIconUM.Currency(
|
||||
CurrencyIconState.CoinIcon(
|
||||
url = null,
|
||||
fallbackResId = com.tangem.core.ui.R.drawable.ic_ethereumpow_22,
|
||||
isGrayscale = false,
|
||||
shouldShowCustomBadge = false,
|
||||
),
|
||||
),
|
||||
tokenName = name,
|
||||
tokenSymbol = symbol,
|
||||
accountName = accountName,
|
||||
fiatRate = "$98,765.43",
|
||||
cryptoBalance = "1.234 $symbol",
|
||||
fiatBalance = "$121,876.50",
|
||||
isBalanceHidden = false,
|
||||
onClick = {},
|
||||
)
|
||||
|
||||
|
|
@ -251,13 +253,8 @@ internal object SearchContentPreviewFixtures {
|
|||
)
|
||||
|
||||
private fun portfolioTwo(): ImmutableList<UserAssetItemUM> = persistentListOf(
|
||||
userAsset(id = "p1", name = "Ethereum", symbol = "ETH", accountName = "Main wallet"),
|
||||
userAsset(
|
||||
id = "p2",
|
||||
name = "Polygon",
|
||||
symbol = "POL",
|
||||
accountName = "Account with a long label for preview",
|
||||
),
|
||||
userAsset(id = "p1", name = "Ethereum", symbol = "ETH"),
|
||||
userAsset(id = "p2", name = "Polygon", symbol = "POL"),
|
||||
)
|
||||
|
||||
private fun marketListShort(): ImmutableList<MarketsListItemUM> = persistentListOf(
|
||||
|
|
@ -390,6 +387,7 @@ private val SearchContentPreviewCallbacks = SearchCallbacks(
|
|||
onClearHintsClick = {},
|
||||
onTextHintClick = { _ -> },
|
||||
onResultMarketTokenClick = { _ -> },
|
||||
onHistoryTokenClick = { _ -> },
|
||||
)
|
||||
|
||||
/** All [SearchContentPreviewScenario] values for the Preview Parameter dropdown in Android Studio. */
|
||||
|
|
|
|||
|
|
@ -7,4 +7,5 @@ internal data class SearchCallbacks(
|
|||
val onClearHintsClick: () -> Unit,
|
||||
val onTextHintClick: (hint: String) -> Unit,
|
||||
val onResultMarketTokenClick: (MarketsListItemUM) -> Unit,
|
||||
val onHistoryTokenClick: (MarketsListItemUM) -> Unit,
|
||||
)
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
package com.tangem.features.feed.ui.search.state
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.common.ui.account.AccountNameUM
|
||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
||||
import com.tangem.core.ui.components.fields.entity.SearchBarUM
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.domain.models.account.CryptoPortfolioIcon
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class SearchUM(
|
||||
|
|
@ -42,11 +45,45 @@ sealed interface MarketSearchResultUM {
|
|||
|
||||
data class TextHintItemUM(val text: String)
|
||||
|
||||
data class UserAssetItemUM(
|
||||
val id: String,
|
||||
val tokenIconUrl: String?,
|
||||
val tokenName: String,
|
||||
val tokenSymbol: String,
|
||||
val accountName: String,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
@Immutable
|
||||
sealed interface UserAssetItemUM {
|
||||
val id: String
|
||||
val icon: TangemIconUM
|
||||
val tokenName: String
|
||||
val tokenSymbol: String
|
||||
val onClick: () -> Unit
|
||||
|
||||
data class Single(
|
||||
override val id: String,
|
||||
override val icon: TangemIconUM,
|
||||
override val tokenName: String,
|
||||
override val tokenSymbol: String,
|
||||
val fiatRate: String?,
|
||||
val cryptoBalance: String,
|
||||
val fiatBalance: String,
|
||||
val isBalanceHidden: Boolean,
|
||||
override val onClick: () -> Unit,
|
||||
) : UserAssetItemUM
|
||||
|
||||
data class Grouped(
|
||||
override val id: String,
|
||||
override val icon: TangemIconUM,
|
||||
override val tokenName: String,
|
||||
override val tokenSymbol: String,
|
||||
val tokensCount: Int,
|
||||
val totalCryptoBalance: String,
|
||||
val totalFiatBalance: String,
|
||||
val isBalanceHidden: Boolean,
|
||||
val children: ImmutableList<GroupedChild>,
|
||||
override val onClick: () -> Unit,
|
||||
) : UserAssetItemUM
|
||||
|
||||
data class GroupedChild(
|
||||
val walletName: String,
|
||||
val accountName: AccountNameUM,
|
||||
val accountIcon: CryptoPortfolioIcon.Icon,
|
||||
val accountColor: CryptoPortfolioIcon.Color,
|
||||
val cryptoBalance: String,
|
||||
val fiatBalance: String,
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue