diff --git a/app/build.gradle b/app/build.gradle index 604ffc9e39..b299a3e4d9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -336,6 +336,10 @@ dependencies { implementation "androidx.compose.runtime:runtime-livedata:$compose_version" + //Material 3 + implementation 'com.google.android.material:material:1.12.0' + implementation "androidx.compose.material3:material3:1.3.1" + def coil_version = "2.6.0" implementation "io.coil-kt:coil-compose:$coil_version" implementation "io.coil-kt:coil-svg:$coil_version" diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filters/MarketFiltersFragment.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filters/MarketFiltersFragment.kt index a1b1f566c3..db3042e14b 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filters/MarketFiltersFragment.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filters/MarketFiltersFragment.kt @@ -13,12 +13,11 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Icon -import androidx.compose.material.ModalBottomSheetLayout -import androidx.compose.material.ModalBottomSheetValue import androidx.compose.material.Scaffold -import androidx.compose.material.rememberModalBottomSheetState +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -45,10 +44,10 @@ import io.horizontalsystems.bankwallet.ui.compose.TranslatableString import io.horizontalsystems.bankwallet.ui.compose.components.AppBar import io.horizontalsystems.bankwallet.ui.compose.components.ButtonPrimaryYellowWithSpinner import io.horizontalsystems.bankwallet.ui.compose.components.CellUniversalLawrenceSection -import io.horizontalsystems.bankwallet.ui.compose.components.HeaderText import io.horizontalsystems.bankwallet.ui.compose.components.HsBackButton import io.horizontalsystems.bankwallet.ui.compose.components.HsSwitch import io.horizontalsystems.bankwallet.ui.compose.components.MenuItem +import io.horizontalsystems.bankwallet.ui.compose.components.PremiumHeader 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 @@ -56,6 +55,7 @@ import io.horizontalsystems.bankwallet.ui.compose.components.body_leah import io.horizontalsystems.bankwallet.ui.compose.components.body_lucian import io.horizontalsystems.bankwallet.ui.compose.components.body_remus import io.horizontalsystems.bankwallet.ui.compose.components.cell.CellUniversal +import io.horizontalsystems.bankwallet.ui.compose.components.cell.SectionPremiumUniversalLawrence import io.horizontalsystems.bankwallet.ui.compose.components.cell.SectionUniversalLawrence import io.horizontalsystems.bankwallet.ui.compose.components.subhead2_grey import io.horizontalsystems.bankwallet.ui.extensions.BottomSheetHeader @@ -79,7 +79,7 @@ class MarketFiltersFragment : BaseComposeFragment() { } -@OptIn(ExperimentalMaterialApi::class) +@OptIn(ExperimentalMaterial3Api::class) @Composable private fun AdvancedSearchScreen( viewModel: MarketFiltersViewModel, @@ -90,78 +90,85 @@ private fun AdvancedSearchScreen( val coroutineScope = rememberCoroutineScope() var bottomSheetType by remember { mutableStateOf(CoinSet) } - val modalBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden) + val modalBottomSheetState = rememberModalBottomSheetState() + var isBottomSheetVisible by remember { mutableStateOf(false) } - ModalBottomSheetLayout( - sheetState = modalBottomSheetState, - sheetBackgroundColor = ComposeAppTheme.colors.transparent, - sheetContent = { + Scaffold( + backgroundColor = ComposeAppTheme.colors.tyler, + topBar = { + AppBar( + title = stringResource(R.string.Market_Filters), + navigationIcon = { + HsBackButton(onClick = { navController.popBackStack() }) + }, + menuItems = listOf( + MenuItem( + title = TranslatableString.ResString(R.string.Button_Reset), + onClick = { viewModel.reset() } + ) + ), + ) + } + ) { paddingValues -> + Column( + modifier = Modifier.padding(paddingValues) + ) { + Column( + modifier = Modifier + .weight(1f) + .verticalScroll(rememberScrollState()) + ) { + AdvancedSearchContent( + viewModel = viewModel, + onFilterByBlockchainsClick = { + navController.slideFromRight(R.id.blockchainsSelectorFragment) + }, + showBottomSheet = { type -> + bottomSheetType = type + coroutineScope.launch { + modalBottomSheetState.show() + isBottomSheetVisible = true + } + } + ) + } + + ButtonsGroupWithShade { + ButtonPrimaryYellowWithSpinner( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + title = uiState.buttonTitle, + onClick = { + navController.slideFromRight( + R.id.marketAdvancedSearchResultsFragment + ) + }, + showSpinner = uiState.showSpinner, + enabled = uiState.buttonEnabled, + ) + } + } + } + + if (isBottomSheetVisible) { + ModalBottomSheet( + onDismissRequest = { + isBottomSheetVisible = false + }, + sheetState = modalBottomSheetState, + containerColor = ComposeAppTheme.colors.transparent + ) { BottomSheetContent( bottomSheetType = bottomSheetType, viewModel = viewModel, onClose = { coroutineScope.launch { modalBottomSheetState.hide() + isBottomSheetVisible = false } } ) - }, - ) { - Scaffold( - backgroundColor = ComposeAppTheme.colors.tyler, - topBar = { - AppBar( - title = stringResource(R.string.Market_Filters), - navigationIcon = { - HsBackButton(onClick = { navController.popBackStack() }) - }, - menuItems = listOf( - MenuItem( - title = TranslatableString.ResString(R.string.Button_Reset), - onClick = { viewModel.reset() } - ) - ), - ) - } - ) { paddingValues -> - Column( - modifier = Modifier.padding(paddingValues) - ) { - Column( - modifier = Modifier - .weight(1f) - .verticalScroll(rememberScrollState()) - ) { - AdvancedSearchContent( - viewModel = viewModel, - onFilterByBlockchainsClick = { - navController.slideFromRight(R.id.blockchainsSelectorFragment) - }, - showBottomSheet = { type -> - bottomSheetType = type - coroutineScope.launch { - modalBottomSheetState.show() - } - } - ) - } - - ButtonsGroupWithShade { - ButtonPrimaryYellowWithSpinner( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - title = uiState.buttonTitle, - onClick = { - navController.slideFromRight( - R.id.marketAdvancedSearchResultsFragment - ) - }, - showSpinner = uiState.showSpinner, - enabled = uiState.buttonEnabled, - ) - } - } } } @@ -178,6 +185,19 @@ private fun BottomSheetContent( ) { val uiState = viewModel.uiState when (bottomSheetType) { + PriceCloseTo -> { + SingleSelectBottomSheetContent( + title = R.string.Market_Filter_PriceCloseTo, + headerIcon = R.drawable.ic_usd_24, + items = viewModel.priceCloseToOptions, + selectedItem = uiState.priceCloseTo, + onSelect = { + viewModel.updatePriceCloseTo(it) + }, + onClose = onClose + ) + } + CoinSet -> { SingleSelectBottomSheetContent( title = R.string.Market_Filter_ChooseSet, @@ -266,19 +286,8 @@ fun AdvancedSearchContent( ) { val uiState = viewModel.uiState - VSpacer(height = 12.dp) + VSpacer(12.dp) - SectionUniversalLawrence { - AdvancedSearchDropdown( - title = R.string.Market_Filter_ChooseSet, - value = uiState.coinListSet.title, - borderTop = false, - onDropdownClick = { showBottomSheet(CoinSet) } - ) - } - VSpacer(height = 32.dp) - - HeaderText(stringResource(R.string.Market_FilterSection_MarketParameters)) SectionUniversalLawrence { AdvancedSearchDropdown( title = R.string.Market_Filter_MarketCap, @@ -291,34 +300,18 @@ fun AdvancedSearchContent( value = uiState.volume.title, onDropdownClick = { showBottomSheet(TradingVolume) } ) - AdvancedSearchSwitch( - title = R.string.Market_Filter_ListedOnTopExchanges, - enabled = uiState.listedOnTopExchangesOn, - onChecked = { viewModel.updateListedOnTopExchangesOn(it) } - ) - AdvancedSearchSwitch( - title = R.string.Market_Filter_SolidCex, - subtitle = R.string.Market_Filter_SolidCex_Description, - enabled = uiState.solidCexOn, - onChecked = { viewModel.updateSolidCexOn(it) } - ) - AdvancedSearchSwitch( - title = R.string.Market_Filter_SolidDex, - subtitle = R.string.Market_Filter_SolidDex_Description, - enabled = uiState.solidDexOn, - onChecked = { viewModel.updateSolidDexOn(it) } - ) - AdvancedSearchSwitch( - title = R.string.Market_Filter_GoodDistribution, - subtitle = R.string.Market_Filter_GoodDistribution_Description, - enabled = uiState.goodDistributionOn, - onChecked = { viewModel.updateGoodDistributionOn(it) } + AdvancedSearchDropdown( + title = R.string.Market_Filter_Blockchains, + value = uiState.selectedBlockchainsValue, + onDropdownClick = onFilterByBlockchainsClick ) } - VSpacer(height = 32.dp) - HeaderText(stringResource(R.string.Market_FilterSection_PriceParameters)) - SectionUniversalLawrence { + VSpacer(24.dp) + + PremiumHeader() + + SectionPremiumUniversalLawrence { AdvancedSearchDropdown( title = R.string.Market_Filter_PriceChange, value = uiState.priceChange.title, @@ -330,6 +323,21 @@ fun AdvancedSearchContent( value = uiState.period.title, onDropdownClick = { showBottomSheet(PricePeriod) } ) + AdvancedSearchDropdown( + title = R.string.Market_Filter_TradingSignals, + value = uiState.filterTradingSignal.title, + onDropdownClick = { showBottomSheet(TradingSignals) } + ) + AdvancedSearchDropdown( + title = R.string.Market_Filter_PriceCloseTo, + value = uiState.priceCloseTo.title, + onDropdownClick = { showBottomSheet(PriceCloseTo) } + ) + } + + VSpacer(24.dp) + + SectionPremiumUniversalLawrence { AdvancedSearchSwitch( title = R.string.Market_Filter_OutperformedBtc, enabled = uiState.outperformedBtcOn, @@ -345,37 +353,36 @@ fun AdvancedSearchContent( enabled = uiState.outperformedBnbOn, onChecked = { viewModel.updateOutperformedBnbOn(it) } ) + } + + VSpacer(24.dp) + + SectionPremiumUniversalLawrence { + AdvancedSearchSwitch( + title = R.string.Market_Filter_SolidCex, + subtitle = R.string.Market_Filter_SolidCex_Description, + enabled = uiState.solidCexOn, + onChecked = { viewModel.updateSolidCexOn(it) } + ) AdvancedSearchSwitch( - title = R.string.Market_Filter_PriceCloseToAth, - enabled = uiState.priceCloseToAth, - onChecked = { viewModel.updateOutperformedAthOn(it) } + title = R.string.Market_Filter_SolidDex, + subtitle = R.string.Market_Filter_SolidDex_Description, + enabled = uiState.solidDexOn, + onChecked = { viewModel.updateSolidDexOn(it) } ) AdvancedSearchSwitch( - title = R.string.Market_Filter_PriceCloseToAtl, - enabled = uiState.priceCloseToAtl, - onChecked = { viewModel.updateOutperformedAtlOn(it) } + title = R.string.Market_Filter_GoodDistribution, + subtitle = R.string.Market_Filter_GoodDistribution_Description, + enabled = uiState.goodDistributionOn, + onChecked = { viewModel.updateGoodDistributionOn(it) } ) - } - VSpacer(height = 32.dp) - - HeaderText(stringResource(R.string.Market_FilterSection_NetworkParameters)) - SectionUniversalLawrence { - AdvancedSearchDropdown( - title = R.string.Market_Filter_Blockchains, - value = uiState.selectedBlockchainsValue, - onDropdownClick = onFilterByBlockchainsClick + AdvancedSearchSwitch( + title = R.string.Market_Filter_ListedOnTopExchanges, + enabled = uiState.listedOnTopExchangesOn, + onChecked = { viewModel.updateListedOnTopExchangesOn(it) } ) } - VSpacer(height = 32.dp) - HeaderText(stringResource(R.string.Market_FilterSection_Indicators)) - SectionUniversalLawrence { - AdvancedSearchDropdown( - title = R.string.Market_Filter_TradingSignals, - value = uiState.filterTradingSignal.title, - onDropdownClick = { showBottomSheet(TradingSignals) } - ) - } VSpacer(32.dp) } diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filters/MarketFiltersModule.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filters/MarketFiltersModule.kt index 7c085ff572..efb4f4a23a 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filters/MarketFiltersModule.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filters/MarketFiltersModule.kt @@ -30,6 +30,7 @@ object MarketFiltersModule { PriceChange(R.string.Market_Filter_PriceChange), PricePeriod(R.string.Market_Filter_PricePeriod), TradingSignals(R.string.Market_Filter_TradingSignals), + PriceCloseTo(R.string.Market_Filter_PriceCloseTo), } data class BlockchainViewItem(val blockchain: Blockchain, val checked: Boolean) @@ -164,6 +165,11 @@ enum class TimePeriod(@StringRes val titleResId: Int): WithTranslatableTitle { get() = TranslatableString.ResString(titleResId) } +enum class PriceCloseTo(val titleResId: Int) { + Ath(R.string.Market_Filter_ATH), + Atl(R.string.Market_Filter_ATL), +} + enum class PriceChange( @StringRes val titleResId: Int, val color: TextColor, diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filters/MarketFiltersViewModel.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filters/MarketFiltersViewModel.kt index 7445a5b2bf..56fe807038 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filters/MarketFiltersViewModel.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/filters/MarketFiltersViewModel.kt @@ -29,11 +29,10 @@ class MarketFiltersViewModel(val service: MarketFiltersService) private var marketCap = rangeEmpty private var volume = rangeEmpty private var priceChange = FilterViewItemWrapper.getAny() + private var priceCloseTo = FilterViewItemWrapper.getAny() private var outperformedBtcOn = false private var outperformedEthOn = false private var outperformedBnbOn = false - private var priceCloseToAth = false - private var priceCloseToAtl = false private var listedOnTopExchangesOn = false private var solidCexOn = false private var solidDexOn = false @@ -56,6 +55,10 @@ class MarketFiltersViewModel(val service: MarketFiltersService) val periodViewItemOptions = TimePeriod.values().map { FilterViewItemWrapper(Translator.getString(it.titleResId), it) } + val priceCloseToOptions = listOf(FilterViewItemWrapper.getAny()) + + PriceCloseTo.entries.map { + FilterViewItemWrapper(Translator.getString(it.titleResId), it) + } val tradingSignals = listOf(FilterViewItemWrapper.getAny()) + FilterTradingSignal.values().map { FilterViewItemWrapper(Translator.getString(it.titleResId), it) } @@ -77,11 +80,10 @@ class MarketFiltersViewModel(val service: MarketFiltersService) marketCap = marketCap, volume = volume, priceChange = priceChange, + priceCloseTo = priceCloseTo, outperformedBtcOn = outperformedBtcOn, outperformedEthOn = outperformedEthOn, outperformedBnbOn = outperformedBnbOn, - priceCloseToAth = priceCloseToAth, - priceCloseToAtl = priceCloseToAtl, selectedBlockchainsValue = selectedBlockchainsValue, selectedBlockchains = selectedBlockchains, blockchainOptions = blockchainOptions, @@ -113,8 +115,6 @@ class MarketFiltersViewModel(val service: MarketFiltersService) outperformedBtcOn = false outperformedEthOn = false outperformedBnbOn = false - priceCloseToAth = false - priceCloseToAtl = false listedOnTopExchangesOn = false solidCexOn = false solidDexOn = false @@ -135,6 +135,13 @@ class MarketFiltersViewModel(val service: MarketFiltersService) reloadData() } + fun updatePriceCloseTo(value: FilterViewItemWrapper) { + priceCloseTo = value + + emitState() + reloadData() + } + fun updateMarketCap(value: FilterViewItemWrapper) { marketCap = value emitState() @@ -183,18 +190,6 @@ class MarketFiltersViewModel(val service: MarketFiltersService) reloadData() } - fun updateOutperformedAthOn(checked: Boolean) { - priceCloseToAth = checked - emitState() - reloadData() - } - - fun updateOutperformedAtlOn(checked: Boolean) { - priceCloseToAtl = checked - emitState() - reloadData() - } - fun updateListedOnTopExchangesOn(checked: Boolean) { listedOnTopExchangesOn = checked emitState() @@ -252,8 +247,8 @@ class MarketFiltersViewModel(val service: MarketFiltersService) service.filterSolidCex = solidCexOn service.filterSolidDex = solidDexOn service.filterGoodDistribution = goodDistributionOn - service.filterPriceCloseToAth = priceCloseToAth - service.filterPriceCloseToAtl = priceCloseToAtl + service.filterPriceCloseToAth = priceCloseTo.item == PriceCloseTo.Ath + service.filterPriceCloseToAtl = priceCloseTo.item == PriceCloseTo.Atl service.filterBlockchains = selectedBlockchains service.filterTradingSignal = filterTradingSignal.item?.getAdvices() ?: emptyList() @@ -306,11 +301,10 @@ data class MarketFiltersUiState( val marketCap: FilterViewItemWrapper, val volume: FilterViewItemWrapper, val priceChange: FilterViewItemWrapper, + val priceCloseTo: FilterViewItemWrapper, val outperformedBtcOn: Boolean, val outperformedEthOn: Boolean, val outperformedBnbOn: Boolean, - val priceCloseToAth: Boolean, - val priceCloseToAtl: Boolean, val selectedBlockchainsValue: String?, val selectedBlockchains: List, val blockchainOptions: List, diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/ui/compose/components/Header.kt b/app/src/main/java/io/horizontalsystems/bankwallet/ui/compose/components/Header.kt index 2a60e75bb2..5681d889fd 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/ui/compose/components/Header.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/ui/compose/components/Header.kt @@ -132,9 +132,33 @@ fun HeaderSorting( } } +@Composable +fun PremiumHeader() { + Row( + modifier = Modifier + .padding(horizontal = 32.dp) + .height(32.dp) + .padding(top = 1.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + modifier = Modifier + .padding(end = 10.dp) + .size(16.dp), + painter = painterResource(R.drawable.crown_yellow_16), + tint = ComposeAppTheme.colors.jacob, + contentDescription = null, + ) + subhead1_jacob( + text = stringResource(R.string.Premium_Title), + maxLines = 1 + ) + } +} + @Preview @Composable -fun Preview_HeaderText(){ +fun Preview_HeaderText() { ComposeAppTheme { HeaderText( text = "Sample Header", diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/ui/compose/components/cell/CellUniversal.kt b/app/src/main/java/io/horizontalsystems/bankwallet/ui/compose/components/cell/CellUniversal.kt index ace3f8de12..35085eab0e 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/ui/compose/components/cell/CellUniversal.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/ui/compose/components/cell/CellUniversal.kt @@ -1,6 +1,7 @@ package io.horizontalsystems.bankwallet.ui.compose.components.cell import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -18,6 +19,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -89,6 +91,16 @@ fun CellUniversalFixedHeight( } } +@Composable +fun SectionPremiumUniversalLawrence( + content: @Composable() (ColumnScope.() -> Unit), +) { + SectionPremiumUniversal( + backgroundColor = ComposeAppTheme.colors.lawrence, + content = content + ) +} + @Composable fun SectionUniversalLawrence( content: @Composable() (ColumnScope.() -> Unit), @@ -99,6 +111,26 @@ fun SectionUniversalLawrence( ) } +@Composable +private fun SectionPremiumUniversal( + backgroundColor: Color, + content: @Composable() (ColumnScope.() -> Unit), +) { + val brush = Brush.horizontalGradient( + 0.0f to Color(0xFFFFD000), + 1.0f to Color(0xFFFFA800), + ) + + Column( + modifier = Modifier + .padding(horizontal = 16.dp) + .clip(RoundedCornerShape(12.dp)) + .border(0.5.dp, brush, RoundedCornerShape(12.dp)) + .background(backgroundColor), + content = content + ) +} + @Composable private fun SectionUniversal( backgroundColor: Color, diff --git a/app/src/main/res/drawable/crown_yellow_16.xml b/app/src/main/res/drawable/crown_yellow_16.xml new file mode 100644 index 0000000000..0f08ee61dd --- /dev/null +++ b/app/src/main/res/drawable/crown_yellow_16.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 94229e2cd8..6d1eac3d56 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -256,6 +256,7 @@ Outperformed BTC Outperformed ETH Outperformed BNB + Price Close To Price Close To ATH Price Close To ATL Listed on Top Exchanges @@ -271,6 +272,8 @@ Strong Sell Sell Risky + ATH + ATL Top 100 Top 250