From d737a25cf9055f03d36a6ee5a78e3b99490863c9 Mon Sep 17 00:00:00 2001 From: Rafael Date: Thu, 30 May 2024 17:45:38 +0600 Subject: [PATCH] Add sorting to Market Filters page --- .../modules/market/MarketViewItem.kt | 5 +- .../modules/market/etf/EtfFragment.kt | 2 +- .../MarketFiltersResultService.kt | 19 ++- .../MarketFiltersResultViewModel.kt | 119 ++++++++---------- .../MarketFiltersResultsFragment.kt | 96 ++++++-------- .../market/metricspage/MetricsPageFragment.kt | 2 +- .../overview/ui/MarketOverviewBoards.kt | 4 +- .../compose/components/CoinListComponents.kt | 6 +- .../compose/components/CoinListOrderable.kt | 24 ++-- .../ui/compose/components/MarketCell.kt | 18 +-- 10 files changed, 128 insertions(+), 167 deletions(-) diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/MarketViewItem.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/MarketViewItem.kt index 879b084ac1a..5269d17f408 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/MarketViewItem.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/MarketViewItem.kt @@ -11,7 +11,8 @@ import io.horizontalsystems.marketkit.models.FullCoin @Immutable data class MarketViewItem( val fullCoin: FullCoin, - val coinRate: String, + val subtitle: String, + val value: String, val marketDataValue: MarketDataValue, val rank: String?, val favorited: Boolean, @@ -52,6 +53,7 @@ data class MarketViewItem( ): MarketViewItem { return MarketViewItem( marketItem.fullCoin, + marketItem.fullCoin.coin.name, App.numberFormatter.formatFiatFull( marketItem.rate.value, marketItem.rate.currency.symbol @@ -93,6 +95,7 @@ data class MarketViewItem( } return MarketViewItem( marketItem.fullCoin, + marketItem.fullCoin.coin.name, App.numberFormatter.formatFiatFull( marketItem.rate.value, marketItem.rate.currency.symbol diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/etf/EtfFragment.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/etf/EtfFragment.kt index aaea82b6185..86f60175ca9 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/etf/EtfFragment.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/etf/EtfFragment.kt @@ -181,8 +181,8 @@ fun EtfPage( } items(uiState.viewItems) { viewItem -> MarketCoinClear( - subtitle = viewItem.subtitle, title = viewItem.title, + subtitle = viewItem.subtitle, coinIconUrl = viewItem.iconUrl, coinIconPlaceholder = R.drawable.ic_platform_placeholder_24, value = viewItem.value, diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filtersresult/MarketFiltersResultService.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filtersresult/MarketFiltersResultService.kt index b4e4d7a4356..8d6b486b0be 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filtersresult/MarketFiltersResultService.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filtersresult/MarketFiltersResultService.kt @@ -2,14 +2,11 @@ package io.horizontalsystems.bankwallet.modules.market.filtersresult import io.horizontalsystems.bankwallet.core.managers.MarketFavoritesManager import io.horizontalsystems.bankwallet.entities.DataState -import io.horizontalsystems.bankwallet.modules.market.MarketField import io.horizontalsystems.bankwallet.modules.market.MarketItem import io.horizontalsystems.bankwallet.modules.market.SortingField -import io.horizontalsystems.bankwallet.modules.market.category.MarketCategoryModule import io.horizontalsystems.bankwallet.modules.market.category.MarketItemWrapper import io.horizontalsystems.bankwallet.modules.market.filters.IMarketListFetcher import io.horizontalsystems.bankwallet.modules.market.sort -import io.horizontalsystems.bankwallet.ui.compose.Select import io.reactivex.subjects.BehaviorSubject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -28,16 +25,14 @@ class MarketFiltersResultService( var marketItems: List = listOf() - val sortingFields = SortingField.values().toList() - private val marketFields = MarketField.values().toList() - var sortingField = SortingField.HighestCap - var marketField = MarketField.PriceDiff + val sortingFields = listOf( + SortingField.HighestCap, + SortingField.LowestCap, + SortingField.TopGainers, + SortingField.TopLosers, + ) - val menu: MarketCategoryModule.Menu - get() = MarketCategoryModule.Menu( - Select(sortingField, sortingFields), - Select(marketField, marketFields) - ) + var sortingField = SortingField.HighestCap private val coroutineScope = CoroutineScope(Dispatchers.Default) private var fetchJob: Job? = null diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filtersresult/MarketFiltersResultViewModel.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filtersresult/MarketFiltersResultViewModel.kt index 5b6693f2048..e27422aaf96 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filtersresult/MarketFiltersResultViewModel.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filtersresult/MarketFiltersResultViewModel.kt @@ -1,55 +1,51 @@ package io.horizontalsystems.bankwallet.modules.market.filtersresult -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import io.horizontalsystems.bankwallet.core.stats.StatEvent -import io.horizontalsystems.bankwallet.core.stats.StatPage -import io.horizontalsystems.bankwallet.core.stats.stat -import io.horizontalsystems.bankwallet.core.stats.statField -import io.horizontalsystems.bankwallet.entities.DataState +import io.horizontalsystems.bankwallet.core.App +import io.horizontalsystems.bankwallet.core.ViewModelUiState import io.horizontalsystems.bankwallet.entities.ViewState -import io.horizontalsystems.bankwallet.modules.market.MarketField +import io.horizontalsystems.bankwallet.modules.market.MarketDataValue import io.horizontalsystems.bankwallet.modules.market.MarketViewItem import io.horizontalsystems.bankwallet.modules.market.SortingField import io.horizontalsystems.bankwallet.modules.market.category.MarketItemWrapper -import io.horizontalsystems.bankwallet.modules.market.topcoins.SelectorDialogState import io.horizontalsystems.bankwallet.ui.compose.Select import kotlinx.coroutines.launch import kotlinx.coroutines.rx2.asFlow class MarketFiltersResultViewModel( private val service: MarketFiltersResultService, -) : ViewModel() { +) : ViewModelUiState() { private var marketItems: List = listOf() - - var viewState by mutableStateOf(ViewState.Loading) - private set - - var viewItemsState by mutableStateOf>(listOf()) - private set - - var selectorDialogState by mutableStateOf(SelectorDialogState.Closed) - private set - - var menuState by mutableStateOf(service.menu) - private set + private var viewState: ViewState = ViewState.Loading + private var viewItemsState: List = listOf() init { - syncMenu() - viewModelScope.launch { - service.stateObservable.asFlow().collect { - syncState(it) + service.stateObservable.asFlow().collect { state -> + state.viewState?.let { + viewState = it + emitState() + } + + state.dataOrNull?.let { + marketItems = it + syncMarketViewItems() + emitState() + } } } service.start() } + override fun createState() = MarketFiltersUiState( + viewItems = viewItemsState, + viewState = viewState, + sortingField = service.sortingField, + selectSortingField = Select(service.sortingField, service.sortingFields) + ) + override fun onCleared() { service.stop() } @@ -58,28 +54,9 @@ class MarketFiltersResultViewModel( service.refresh() } - fun showSelectorMenu() { - selectorDialogState = - SelectorDialogState.Opened(Select(service.sortingField, service.sortingFields)) - } - - fun onSelectorDialogDismiss() { - selectorDialogState = SelectorDialogState.Closed - } - fun onSelectSortingField(sortingField: SortingField) { service.updateSortingField(sortingField) - selectorDialogState = SelectorDialogState.Closed - syncMenu() - } - - fun marketFieldSelected(marketField: MarketField) { - service.marketField = marketField - - syncMarketViewItems() - syncMenu() - - stat(page = StatPage.AdvancedSearchResults, event = StatEvent.SwitchField(marketField.statField)) + emitState() } fun onAddFavorite(uid: String) { @@ -90,30 +67,32 @@ class MarketFiltersResultViewModel( service.removeFavorite(uid) } - private fun syncState(state: DataState>) { - viewModelScope.launch { - state.viewState?.let { - viewState = it - } - - state.dataOrNull?.let { - marketItems = it - - syncMarketViewItems() - } - - syncMenu() - } - } - - private fun syncMenu() { - menuState = service.menu - } - private fun syncMarketViewItems() { - viewItemsState = marketItems.map { - MarketViewItem.create(it.marketItem, service.marketField, it.favorited) + viewItemsState = marketItems.map { itemWrapper -> + val marketCap = App.numberFormatter.formatFiatShort( + itemWrapper.marketItem.marketCap.value, + itemWrapper.marketItem.marketCap.currency.symbol, + 2 + ) + MarketViewItem( + fullCoin = itemWrapper.marketItem.fullCoin, + subtitle = marketCap, + value = App.numberFormatter.formatFiatFull( + itemWrapper.marketItem.rate.value, + itemWrapper.marketItem.rate.currency.symbol + ), + marketDataValue = MarketDataValue.Diff(itemWrapper.marketItem.diff), + rank = itemWrapper.marketItem.rank?.toString(), + favorited = itemWrapper.favorited + ) }.toList() } } + +data class MarketFiltersUiState( + val viewItems: List, + val viewState: ViewState, + val sortingField: SortingField, + val selectSortingField: Select +) \ No newline at end of file diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filtersresult/MarketFiltersResultsFragment.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filtersresult/MarketFiltersResultsFragment.kt index eef60895c35..5c80fea28bb 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filtersresult/MarketFiltersResultsFragment.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filtersresult/MarketFiltersResultsFragment.kt @@ -2,20 +2,13 @@ package io.horizontalsystems.bankwallet.modules.market.filtersresult import androidx.compose.animation.Crossfade import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding import androidx.compose.material.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.fragment.app.viewModels @@ -31,16 +24,15 @@ import io.horizontalsystems.bankwallet.entities.ViewState import io.horizontalsystems.bankwallet.modules.coin.CoinFragment import io.horizontalsystems.bankwallet.modules.coin.overview.ui.Loading import io.horizontalsystems.bankwallet.modules.market.filters.MarketFiltersViewModel -import io.horizontalsystems.bankwallet.modules.market.topcoins.SelectorDialogState +import io.horizontalsystems.bankwallet.modules.market.topcoins.OptionController import io.horizontalsystems.bankwallet.ui.compose.ComposeAppTheme import io.horizontalsystems.bankwallet.ui.compose.components.AlertGroup import io.horizontalsystems.bankwallet.ui.compose.components.AppBar -import io.horizontalsystems.bankwallet.ui.compose.components.ButtonSecondaryToggle import io.horizontalsystems.bankwallet.ui.compose.components.CoinList +import io.horizontalsystems.bankwallet.ui.compose.components.HSpacer import io.horizontalsystems.bankwallet.ui.compose.components.HeaderSorting import io.horizontalsystems.bankwallet.ui.compose.components.HsBackButton import io.horizontalsystems.bankwallet.ui.compose.components.ListErrorView -import io.horizontalsystems.bankwallet.ui.compose.components.SortMenu class MarketFiltersResultsFragment : BaseComposeFragment() { @@ -77,7 +69,9 @@ private fun SearchResultsScreen( navController: NavController ) { + val uiState = viewModel.uiState var scrollToTopAfterUpdate by rememberSaveable { mutableStateOf(false) } + var openSortingSelector by rememberSaveable { mutableStateOf(false) } Surface(color = ComposeAppTheme.colors.tyler) { Column { @@ -88,7 +82,7 @@ private fun SearchResultsScreen( }, ) - Crossfade(viewModel.viewState) { state -> + Crossfade(uiState.viewState, label = "") { state -> when (state) { ViewState.Loading -> { Loading() @@ -100,27 +94,45 @@ private fun SearchResultsScreen( ViewState.Success -> { CoinList( - items = viewModel.viewItemsState, + items = uiState.viewItems, scrollToTop = scrollToTopAfterUpdate, onAddFavorite = { uid -> viewModel.onAddFavorite(uid) - stat(page = StatPage.AdvancedSearchResults, event = StatEvent.AddToWatchlist(uid)) + stat( + page = StatPage.AdvancedSearchResults, + event = StatEvent.AddToWatchlist(uid) + ) }, onRemoveFavorite = { uid -> viewModel.onRemoveFavorite(uid) - stat(page = StatPage.AdvancedSearchResults, event = StatEvent.RemoveFromWatchlist(uid)) + stat( + page = StatPage.AdvancedSearchResults, + event = StatEvent.RemoveFromWatchlist(uid) + ) }, onCoinClick = { coinUid -> val arguments = CoinFragment.Input(coinUid) navController.slideFromRight(R.id.coinFragment, arguments) - stat(page = StatPage.AdvancedSearchResults, event = StatEvent.OpenCoin(coinUid)) + stat( + page = StatPage.AdvancedSearchResults, + event = StatEvent.OpenCoin(coinUid) + ) }, preItems = { stickyHeader { - ListHeaderMenu(viewModel) + HeaderSorting(borderBottom = true, borderTop = true) { + HSpacer(width = 16.dp) + OptionController( + uiState.sortingField.titleResId, + onOptionClick = { + openSortingSelector = true + } + ) + HSpacer(width = 16.dp) + } } } ) @@ -131,49 +143,21 @@ private fun SearchResultsScreen( } } - } - - //Dialog - (viewModel.selectorDialogState as? SelectorDialogState.Opened)?.let { state -> - AlertGroup( - title = R.string.Market_Sort_PopupTitle, - select = state.select, - onSelect = { selected -> - scrollToTopAfterUpdate = true - viewModel.onSelectSortingField(selected) - }, - onDismiss = { viewModel.onSelectorDialogDismiss() } - ) - } - } - -} - -@Composable -private fun ListHeaderMenu( - viewModel: MarketFiltersResultViewModel, -) { - HeaderSorting(borderTop = true, borderBottom = true) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(end = 16.dp) - .height(44.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Box(modifier = Modifier.weight(1f)) { - SortMenu( - titleRes = viewModel.menuState.sortingFieldSelect.selected.titleResId, - onClick = viewModel::showSelectorMenu + if (openSortingSelector) { + AlertGroup( + title = R.string.Market_Sort_PopupTitle, + select = uiState.selectSortingField, + onSelect = { selected -> + viewModel.onSelectSortingField(selected) + openSortingSelector = false + scrollToTopAfterUpdate = true + }, + onDismiss = { + openSortingSelector = false + } ) } - Box(modifier = Modifier.padding(start = 8.dp)) { - ButtonSecondaryToggle( - select = viewModel.menuState.marketFieldSelect, - onSelect = viewModel::marketFieldSelected - ) - } } } } diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/metricspage/MetricsPageFragment.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/metricspage/MetricsPageFragment.kt index c6ebf28828a..94e40c72a29 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/metricspage/MetricsPageFragment.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/metricspage/MetricsPageFragment.kt @@ -152,8 +152,8 @@ class MetricsPageFragment : BaseComposeFragment() { } items(uiState.viewItems) { viewItem -> MarketCoinClear( - subtitle = viewItem.subtitle, title = viewItem.fullCoin.coin.code, + subtitle = viewItem.subtitle, coinIconUrl = viewItem.fullCoin.coin.imageUrl, alternativeCoinIconUrl = viewItem.fullCoin.coin.alternativeImageUrl, coinIconPlaceholder = viewItem.fullCoin.iconPlaceholder, diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/overview/ui/MarketOverviewBoards.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/overview/ui/MarketOverviewBoards.kt index 3e3169679ed..c00df5589b0 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/overview/ui/MarketOverviewBoards.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/overview/ui/MarketOverviewBoards.kt @@ -110,12 +110,12 @@ private fun MarketCoinWithBackground( onClick: () -> Unit ) { MarketCoinClear( - marketViewItem.coinName, marketViewItem.coinCode, + marketViewItem.coinName, marketViewItem.iconUrl, marketViewItem.alternativeIconUrl, marketViewItem.iconPlaceHolder, - marketViewItem.coinRate, + marketViewItem.value, marketViewItem.marketDataValue, marketViewItem.rank, onClick diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/ui/compose/components/CoinListComponents.kt b/app/src/main/java/io/horizontalsystems/bankwallet/ui/compose/components/CoinListComponents.kt index 0ff7cf1f3fc..bed6411adf4 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/ui/compose/components/CoinListComponents.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/ui/compose/components/CoinListComponents.kt @@ -128,12 +128,12 @@ fun CoinList( }, content = { MarketCoin( - coinName = item.fullCoin.coin.name, - coinCode = item.fullCoin.coin.code, + title = item.fullCoin.coin.code, + subtitle = item.subtitle, coinIconUrl = item.fullCoin.coin.imageUrl, alternativeCoinIconUrl = item.fullCoin.coin.alternativeImageUrl, coinIconPlaceholder = item.fullCoin.iconPlaceholder, - coinRate = item.coinRate, + value = item.value, marketDataValue = item.marketDataValue, label = item.rank, advice = item.signal, diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/ui/compose/components/CoinListOrderable.kt b/app/src/main/java/io/horizontalsystems/bankwallet/ui/compose/components/CoinListOrderable.kt index 9c8f01dd902..53b2e59c747 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/ui/compose/components/CoinListOrderable.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/ui/compose/components/CoinListOrderable.kt @@ -84,15 +84,15 @@ fun CoinListOrderable( } MarketCoin( - item.fullCoin.coin.name, - item.fullCoin.coin.code, - item.fullCoin.coin.imageUrl, - item.fullCoin.coin.alternativeImageUrl, - item.fullCoin.iconPlaceholder, - item.coinRate, - item.marketDataValue, - item.rank, - item.signal, + title = item.fullCoin.coin.code, + subtitle = item.fullCoin.coin.name, + coinIconUrl = item.fullCoin.coin.imageUrl, + alternativeCoinIconUrl = item.fullCoin.coin.alternativeImageUrl, + coinIconPlaceholder = item.fullCoin.iconPlaceholder, + value = item.value, + marketDataValue = item.marketDataValue, + label = item.rank, + advice = item.signal, ) } } else { @@ -140,12 +140,12 @@ fun CoinListOrderable( }, content = { MarketCoin( - coinName = item.fullCoin.coin.name, - coinCode = item.fullCoin.coin.code, + title = item.fullCoin.coin.code, + subtitle = item.fullCoin.coin.name, coinIconUrl = item.fullCoin.coin.imageUrl, alternativeCoinIconUrl = item.fullCoin.coin.alternativeImageUrl, coinIconPlaceholder = item.fullCoin.iconPlaceholder, - coinRate = item.coinRate, + value = item.value, marketDataValue = item.marketDataValue, label = item.rank, advice = item.signal, diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/ui/compose/components/MarketCell.kt b/app/src/main/java/io/horizontalsystems/bankwallet/ui/compose/components/MarketCell.kt index dcb7969c89d..4cbb567987a 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/ui/compose/components/MarketCell.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/ui/compose/components/MarketCell.kt @@ -30,8 +30,8 @@ import io.horizontalsystems.marketkit.models.Analytics.TechnicalAdvice.Advice @Composable fun MarketCoinClear( - subtitle: String, title: String, + subtitle: String, coinIconUrl: String, alternativeCoinIconUrl: String? = null, coinIconPlaceholder: Int, @@ -65,12 +65,12 @@ fun MarketCoinClear( @OptIn(ExperimentalFoundationApi::class) @Composable fun MarketCoin( - coinName: String, - coinCode: String, + title: String, + subtitle: String, coinIconUrl: String, alternativeCoinIconUrl: String? = null, coinIconPlaceholder: Int, - coinRate: String? = null, + value: String? = null, marketDataValue: MarketDataValue? = null, label: String? = null, advice: Advice? = null, @@ -102,9 +102,9 @@ fun MarketCoin( Column( modifier = Modifier.fillMaxWidth() ) { - MarketCoinFirstRow(coinCode, coinRate, advice) + MarketCoinFirstRow(title, value, advice) Spacer(modifier = Modifier.height(3.dp)) - MarketCoinSecondRow(coinName, marketDataValue, label) + MarketCoinSecondRow(subtitle, marketDataValue, label) } } } @@ -274,11 +274,11 @@ fun MarketDataValueComponent(marketDataValue: MarketDataValue?) { fun PreviewMarketCoin(){ ComposeAppTheme { MarketCoin( - coinName = "Ethereum With very long name for token", - coinCode = "ETH", + title = "ETH", + subtitle = "Ethereum With very long name for token", coinIconUrl = "eth.png", coinIconPlaceholder = R.drawable.logo_ethereum_24, - coinRate = "$2600", + value = "$2600", ) } }