Skip to content

Commit

Permalink
Allow users to select multiple sectors in MarketFilters
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaelekol committed Jan 6, 2025
1 parent ec09e01 commit 51e29ef
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 33 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ dependencies {
implementation 'com.github.horizontalsystems:ethereum-kit-android:0c770e3'
implementation 'com.github.horizontalsystems:blockchain-fee-rate-kit-android:1d3bd49'
implementation 'com.github.horizontalsystems:binance-chain-kit-android:c1509a2'
implementation 'com.github.horizontalsystems:market-kit-android:ef4a068'
implementation 'com.github.horizontalsystems:market-kit-android:e4e90bd'
implementation 'com.github.horizontalsystems:solana-kit-android:ce738d8'
implementation 'com.github.horizontalsystems:tron-kit-android:dc3dca7'
// Zcash SDK
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ class MarketKitWrapper(

fun marketInfosSingle(top: Int, currencyCode: String, defi: Boolean) = marketKit.marketInfosSingle(top, currencyCode, defi)

fun categoriesSingle() = marketKit.getCategories()
fun categoriesSingle() = marketKit.categoriesSingle()

fun advancedMarketInfosSingle(top: Int = 250, currencyCode: String, sectorId: Int?) = marketKit.advancedMarketInfosSingle(top, currencyCode, sectorId)
fun advancedMarketInfosSingle(top: Int = 250, currencyCode: String) = marketKit.advancedMarketInfosSingle(top, currencyCode)

fun marketInfosSingle(coinUids: List<String>, currencyCode: String): Single<List<MarketInfo>> =
marketKit.marketInfosSingle(coinUids.removeCustomCoins(), currencyCode)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import io.horizontalsystems.bankwallet.modules.market.filters.MarketFiltersModul
import io.horizontalsystems.bankwallet.modules.market.filters.MarketFiltersModule.FilterDropdown.PriceChange
import io.horizontalsystems.bankwallet.modules.market.filters.MarketFiltersModule.FilterDropdown.PriceCloseTo
import io.horizontalsystems.bankwallet.modules.market.filters.MarketFiltersModule.FilterDropdown.PricePeriod
import io.horizontalsystems.bankwallet.modules.market.filters.MarketFiltersModule.FilterDropdown.SectorSet
import io.horizontalsystems.bankwallet.modules.market.filters.MarketFiltersModule.FilterDropdown.TradingSignals
import io.horizontalsystems.bankwallet.modules.market.filters.MarketFiltersModule.FilterDropdown.TradingVolume
import io.horizontalsystems.bankwallet.ui.compose.ComposeAppTheme
Expand Down Expand Up @@ -274,19 +273,6 @@ private fun BottomSheetContent(
onClose = onClose
)
}

SectorSet -> {
SingleSelectBottomSheetContent(
title = R.string.Market_Filter_Sectors,
headerIcon = R.drawable.ic_ring_24,
items = viewModel.sectorsViewItemOptions,
selectedItem = uiState.sector,
onSelect = {
viewModel.setSector(it)
},
onClose = onClose
)
}
}
}

Expand Down Expand Up @@ -327,11 +313,9 @@ fun AdvancedSearchContent(
SectionPremiumUniversalLawrence {
AdvancedSearchDropdown(
title = R.string.Market_Filter_Sectors,
value = uiState.sector.title,
value = if (uiState.sectors.size == 1 && uiState.sectors[0].item == null) null else uiState.sectors.size.toString(),
onDropdownClick = {
navController.paidAction(AdvancedSearch) {
showBottomSheet(SectorSet)
}
navController.slideFromRight(R.id.sectorsSelectorFragment)
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ object MarketFiltersModule {
PricePeriod(R.string.Market_Filter_PricePeriod),
TradingSignals(R.string.Market_Filter_TradingSignals),
PriceCloseTo(R.string.Market_Filter_PriceCloseTo),
SectorSet(R.string.Market_Filter_Sectors),
}

data class BlockchainViewItem(val blockchain: Blockchain, val checked: Boolean)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ class MarketFiltersService(
val currencyCode = baseCurrency.code

var coinCount = CoinList.Top200.itemsCount
var sectorId: Int? = null
var filterMarketCap: Pair<Long?, Long?>? = null
var sectorIds: List<Int> = listOf()
var filterVolume: Pair<Long?, Long?>? = null
var filterPeriod = TimePeriod.TimePeriod_1D
var filterPriceChange: Pair<Long?, Long?>? = null
Expand Down Expand Up @@ -91,7 +91,7 @@ class MarketFiltersService(
val topMarketListAsync = if (cache != null) {
Single.just(cache)
} else {
marketKit.advancedMarketInfosSingle(coinCount, baseCurrency.code, sectorId)
marketKit.advancedMarketInfosSingle(coinCount, baseCurrency.code)
.doOnSuccess {
cache = it
}
Expand All @@ -115,6 +115,7 @@ class MarketFiltersService(
return filterByRange(filterMarketCap, marketCap.toLong())
&& filterByRange(filterVolume, totalVolume.toLong())
&& inBlockchain(marketInfo.fullCoin.tokens)
&& inSectors(marketInfo.categoryIds, sectorIds)
&& filterByRange(filterPriceChange, priceChangeValue.toLong())
&& (!filterPriceCloseToAth || closeToAllTime(marketInfo.athPercentage))
&& (!filterPriceCloseToAtl || closeToAllTime(marketInfo.atlPercentage))
Expand Down Expand Up @@ -165,6 +166,12 @@ class MarketFiltersService(
return filterTradingSignal.contains(tokenAdvice)
}

private fun inSectors(ids: List<Int>?, selectedSectorIds: List<Int>): Boolean {
if (selectedSectorIds.isEmpty()) return true
if (ids == null) return false
return ids.intersect(selectedSectorIds.toSet()).isNotEmpty()
}

private fun inBlockchain(tokens: List<Token>): Boolean {
if (filterBlockchains.isEmpty()) return true

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ class MarketFiltersViewModel(val service: MarketFiltersService) :
private var marketCap = rangeEmpty
private var volume = rangeEmpty
private var priceChange = FilterViewItemWrapper.getAny<PriceChange>()
private var selectedSector = FilterViewItemWrapper.getAny<SectorItem>()
private var selectedSectors: List<FilterViewItemWrapper<SectorItem?>> =
listOf(FilterViewItemWrapper.getAny())
private var priceCloseTo: PriceCloseTo? = null
private var outperformedBtcOn = false
private var outperformedEthOn = false
Expand Down Expand Up @@ -86,7 +87,7 @@ class MarketFiltersViewModel(val service: MarketFiltersService) :
marketCap = marketCap,
volume = volume,
priceChange = priceChange,
sector = selectedSector,
sectors = selectedSectors,
priceCloseTo = priceCloseTo,
outperformedBtcOn = outperformedBtcOn,
outperformedEthOn = outperformedEthOn,
Expand Down Expand Up @@ -124,8 +125,8 @@ class MarketFiltersViewModel(val service: MarketFiltersService) :
filterTradingSignal = FilterViewItemWrapper.getAny()
updateSelectedBlockchains()

selectedSector = FilterViewItemWrapper.getAny()
service.sectorId = null
selectedSectors = listOf(FilterViewItemWrapper.getAny())
service.sectorIds = emptyList()
coinListSet = CoinList.Top200
service.coinCount = CoinList.Top200.itemsCount
reloadDataWithSpinner()
Expand All @@ -137,10 +138,16 @@ class MarketFiltersViewModel(val service: MarketFiltersService) :
reloadDataWithSpinner()
}

fun setSector(sectorItem: FilterViewItemWrapper<SectorItem?>) {
selectedSector = sectorItem
service.sectorId = sectorItem.item?.id
reloadDataWithSpinner()
fun setSectors(sectorItems: List<FilterViewItemWrapper<SectorItem?>>) {
if (sectorItems.isEmpty()) {
selectedSectors = listOf(FilterViewItemWrapper.getAny())
service.sectorIds = emptyList()
reloadData()
return
}
selectedSectors = sectorItems
service.sectorIds = sectorItems.mapNotNull { it.item?.id }
reloadData()
}

private fun reloadDataWithSpinner() {
Expand Down Expand Up @@ -316,7 +323,7 @@ data class MarketFiltersUiState(
val marketCap: FilterViewItemWrapper<Range?>,
val volume: FilterViewItemWrapper<Range?>,
val priceChange: FilterViewItemWrapper<PriceChange?>,
val sector: FilterViewItemWrapper<SectorItem?>,
val sectors: List<FilterViewItemWrapper<SectorItem?>>,
val priceCloseTo: PriceCloseTo?,
val outperformedBtcOn: Boolean,
val outperformedEthOn: Boolean,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package io.horizontalsystems.bankwallet.modules.market.filters

import android.os.Bundle
import androidx.activity.OnBackPressedCallback
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.navigation.navGraphViewModels
import io.horizontalsystems.bankwallet.R
import io.horizontalsystems.bankwallet.core.BaseComposeFragment
import io.horizontalsystems.bankwallet.modules.evmfee.ButtonsGroupWithShade
import io.horizontalsystems.bankwallet.ui.compose.ComposeAppTheme
import io.horizontalsystems.bankwallet.ui.compose.components.AppBar
import io.horizontalsystems.bankwallet.ui.compose.components.ButtonPrimaryYellow
import io.horizontalsystems.bankwallet.ui.compose.components.CellUniversalLawrenceSection
import io.horizontalsystems.bankwallet.ui.compose.components.HsBackButton
import io.horizontalsystems.bankwallet.ui.compose.components.RowUniversal
import io.horizontalsystems.bankwallet.ui.compose.components.VSpacer
import io.horizontalsystems.bankwallet.ui.compose.components.body_grey
import io.horizontalsystems.bankwallet.ui.compose.components.body_leah
import io.horizontalsystems.core.findNavController

class SectorsSelectorFragment : BaseComposeFragment() {

private val viewModel by navGraphViewModels<MarketFiltersViewModel>(R.id.marketAdvancedSearchFragment) {
MarketFiltersModule.Factory()
}

@Composable
override fun GetContent(navController: NavController) {
SectorsSelectorScreen(
viewModel,
navController,
)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

requireActivity().onBackPressedDispatcher.addCallback(
this,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
findNavController().popBackStack()
}
})
}

}

@Composable
fun SectorsSelectorScreen(
viewModel: MarketFiltersViewModel,
navController: NavController
) {
val uiState = viewModel.uiState
var selectedItems by remember { mutableStateOf(uiState.sectors) }
val sectorItems = viewModel.sectorsViewItemOptions

Scaffold(
topBar = {
AppBar(
title = stringResource(R.string.Market_Filter_Blockchains),
navigationIcon = {
HsBackButton(onClick = { navController.popBackStack() })
}
)
},
backgroundColor = ComposeAppTheme.colors.tyler,
) {
Column(
modifier = Modifier.padding(it)
) {
Column(
modifier = Modifier
.weight(1f)
.verticalScroll(rememberScrollState())
) {
VSpacer(12.dp)
CellUniversalLawrenceSection(
items = sectorItems,
showFrame = true
) { itemWrapper ->
RowUniversal(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
verticalPadding = 0.dp,
onClick = {
selectedItems =
if (selectedItems.contains(itemWrapper) && itemWrapper.item == null) {
listOf(itemWrapper) //no action when `Any` is already selected and pressed
} else if (!selectedItems.contains(itemWrapper) && itemWrapper.item == null) {
listOf(itemWrapper) //on option `Any` select, reset other selected items
} else if (!selectedItems.contains(itemWrapper) && selectedItems.size == 1 && selectedItems[0].item == null) {
listOf(itemWrapper) //on option select, reset `Any` option
} else if (selectedItems.contains(itemWrapper) && selectedItems.size == 1) {
listOf(sectorItems[0]) // return `Any` option when last selected item is unselected
} else if (selectedItems.contains(itemWrapper)) {
selectedItems - itemWrapper
} else {
selectedItems + itemWrapper
}
}
) {
if (itemWrapper.title != null) {
body_leah(
text = itemWrapper.title,
modifier = Modifier.padding(vertical = 12.dp)
)
} else {
body_grey(
text = stringResource(R.string.Any),
modifier = Modifier.padding(vertical = 12.dp)
)
}

Spacer(modifier = Modifier.weight(1f))
if (itemWrapper in selectedItems) {
Image(
modifier = Modifier.padding(start = 5.dp),
painter = painterResource(id = R.drawable.ic_checkmark_20),
colorFilter = ColorFilter.tint(ComposeAppTheme.colors.jacob),
contentDescription = null
)
}
}
}
VSpacer(24.dp)
}
ButtonsGroupWithShade {
ButtonPrimaryYellow(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp),
title = stringResource(R.string.Button_Apply),
onClick = {
viewModel.setSectors(selectedItems)
navController.popBackStack()
},
)
}
}
}
}
3 changes: 3 additions & 0 deletions app/src/main/res/navigation/main_graph.xml
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,9 @@
<fragment
android:id="@+id/blockchainsSelectorFragment"
android:name="io.horizontalsystems.bankwallet.modules.market.filters.BlockchainsSelectorFragment"/>
<fragment
android:id="@+id/sectorsSelectorFragment"
android:name="io.horizontalsystems.bankwallet.modules.market.filters.SectorsSelectorFragment"/>
<fragment
android:id="@+id/marketCategoryFragment"
android:name="io.horizontalsystems.bankwallet.modules.market.category.MarketCategoryFragment"/>
Expand Down

0 comments on commit 51e29ef

Please sign in to comment.