Skip to content

Commit 0b4091f

Browse files
committed
Merge branch 'filter-chips' into main
2 parents 35d992f + 1684f78 commit 0b4091f

File tree

8 files changed

+152
-95
lines changed

8 files changed

+152
-95
lines changed

app/src/main/java/dev/shorthouse/coinwatch/data/datastore/UserPreferences.kt

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package dev.shorthouse.coinwatch.data.datastore
22

3+
import androidx.annotation.StringRes
4+
import dev.shorthouse.coinwatch.R
35
import kotlinx.serialization.Serializable
46

57
@Serializable
@@ -14,9 +16,9 @@ enum class Currency(val symbol: String) {
1416
EUR("")
1517
}
1618

17-
enum class CoinSort {
18-
MarketCap,
19-
Price,
20-
PriceChange24h,
21-
Volume24h
19+
enum class CoinSort(@StringRes val nameId: Int) {
20+
MarketCap(R.string.coin_sort_market_cap),
21+
Price(R.string.coin_sort_price),
22+
PriceChange24h(R.string.coin_sort_price_change),
23+
Volume24h(R.string.coin_sort_volume)
2224
}

app/src/main/java/dev/shorthouse/coinwatch/ui/previewdata/MarketUiStatePreviewProvider.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class MarketUiStatePreviewProvider : PreviewParameterProvider<MarketUiState> {
2323
),
2424
MarketUiState(
2525
coins = coins,
26-
showCoinCurrencyBottomSheet = true
26+
showCurrencyBottomSheet = true
2727
),
2828
MarketUiState(
2929
errorMessage = "Error message"

app/src/main/java/dev/shorthouse/coinwatch/ui/screen/market/MarketScreen.kt

+86-78
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ package dev.shorthouse.coinwatch.ui.screen.market
33
import androidx.compose.animation.AnimatedVisibility
44
import androidx.compose.animation.scaleIn
55
import androidx.compose.animation.scaleOut
6+
import androidx.compose.foundation.layout.Arrangement
67
import androidx.compose.foundation.layout.Box
7-
import androidx.compose.foundation.layout.PaddingValues
8+
import androidx.compose.foundation.layout.Column
9+
import androidx.compose.foundation.layout.Row
810
import androidx.compose.foundation.layout.Spacer
911
import androidx.compose.foundation.layout.fillMaxSize
1012
import androidx.compose.foundation.layout.padding
@@ -14,13 +16,10 @@ import androidx.compose.foundation.lazy.rememberLazyListState
1416
import androidx.compose.foundation.shape.CornerSize
1517
import androidx.compose.foundation.shape.RoundedCornerShape
1618
import androidx.compose.material.icons.Icons
17-
import androidx.compose.material.icons.rounded.CurrencyExchange
1819
import androidx.compose.material.icons.rounded.KeyboardDoubleArrowUp
19-
import androidx.compose.material.icons.rounded.SwapVert
2020
import androidx.compose.material3.ExperimentalMaterial3Api
2121
import androidx.compose.material3.FloatingActionButtonDefaults
2222
import androidx.compose.material3.Icon
23-
import androidx.compose.material3.IconButton
2423
import androidx.compose.material3.MaterialTheme
2524
import androidx.compose.material3.Scaffold
2625
import androidx.compose.material3.SmallFloatingActionButton
@@ -51,9 +50,11 @@ import dev.shorthouse.coinwatch.ui.component.ErrorState
5150
import dev.shorthouse.coinwatch.ui.component.pullrefresh.PullRefreshIndicator
5251
import dev.shorthouse.coinwatch.ui.component.pullrefresh.pullRefresh
5352
import dev.shorthouse.coinwatch.ui.component.pullrefresh.rememberPullRefreshState
53+
import dev.shorthouse.coinwatch.ui.model.TimeOfDay
5454
import dev.shorthouse.coinwatch.ui.previewdata.MarketUiStatePreviewProvider
5555
import dev.shorthouse.coinwatch.ui.screen.market.component.CoinSortBottomSheet
5656
import dev.shorthouse.coinwatch.ui.screen.market.component.CurrencyBottomSheet
57+
import dev.shorthouse.coinwatch.ui.screen.market.component.MarketChip
5758
import dev.shorthouse.coinwatch.ui.screen.market.component.MarketCoinItem
5859
import dev.shorthouse.coinwatch.ui.screen.market.component.MarketEmptyState
5960
import dev.shorthouse.coinwatch.ui.screen.market.component.MarketSkeletonLoader
@@ -80,11 +81,11 @@ fun MarketScreen(
8081
onUpdateShowCoinSortBottomSheet = { showSheet ->
8182
viewModel.updateShowCoinSortBottomSheet(showSheet)
8283
},
83-
onUpdateCoinCurrency = { currency ->
84-
viewModel.updateCoinCurrency(currency)
84+
onUpdateCurrency = { currency ->
85+
viewModel.updateCurrency(currency)
8586
},
86-
onUpdateShowCoinCurrencyBottomSheet = { showSheet ->
87-
viewModel.updateShowCoinCurrencyBottomSheet(showSheet)
87+
onUpdateShowCurrencyBottomSheet = { showSheet ->
88+
viewModel.onUpdateShowCurrencyBottomSheet(showSheet)
8889
},
8990
onRefresh = {
9091
viewModel.pullRefreshCachedCoins()
@@ -99,8 +100,8 @@ fun MarketScreen(
99100
onCoinClick: (CachedCoin) -> Unit,
100101
onUpdateCoinSort: (CoinSort) -> Unit,
101102
onUpdateShowCoinSortBottomSheet: (Boolean) -> Unit,
102-
onUpdateCoinCurrency: (Currency) -> Unit,
103-
onUpdateShowCoinCurrencyBottomSheet: (Boolean) -> Unit,
103+
onUpdateCurrency: (Currency) -> Unit,
104+
onUpdateShowCurrencyBottomSheet: (Boolean) -> Unit,
104105
onRefresh: () -> Unit,
105106
modifier: Modifier = Modifier
106107
) {
@@ -122,8 +123,7 @@ fun MarketScreen(
122123
Scaffold(
123124
topBar = {
124125
MarketTopBar(
125-
onUpdateShowCoinSortBottomSheet = onUpdateShowCoinSortBottomSheet,
126-
onUpdateShowCoinCurrencyBottomSheet = onUpdateShowCoinCurrencyBottomSheet,
126+
timeOfDay = uiState.timeOfDay,
127127
scrollBehavior = scrollBehavior
128128
)
129129
},
@@ -149,6 +149,10 @@ fun MarketScreen(
149149
MarketContent(
150150
coins = uiState.coins,
151151
onCoinClick = onCoinClick,
152+
currency = uiState.currency,
153+
onUpdateShowCurrencyBottomSheet = onUpdateShowCurrencyBottomSheet,
154+
coinSort = uiState.coinSort,
155+
onUpdateShowCoinSortBottomSheet = onUpdateShowCoinSortBottomSheet,
152156
lazyListState = lazyListState,
153157
modifier = Modifier.padding(scaffoldPadding)
154158
)
@@ -172,22 +176,22 @@ fun MarketScreen(
172176
)
173177
}
174178

175-
if (uiState.showCoinCurrencyBottomSheet) {
179+
if (uiState.showCurrencyBottomSheet) {
176180
CurrencyBottomSheet(
177181
sheetState = currencySheetState,
178-
selectedCurrency = uiState.coinCurrency,
182+
selectedCurrency = uiState.currency,
179183
onCurrencySelected = { currency ->
180-
onUpdateCoinCurrency(currency)
184+
onUpdateCurrency(currency)
181185

182186
scope.launch {
183187
currencySheetState.hide()
184188
}.invokeOnCompletion {
185189
if (!currencySheetState.isVisible) {
186-
onUpdateShowCoinCurrencyBottomSheet(false)
190+
onUpdateShowCurrencyBottomSheet(false)
187191
}
188192
}
189193
},
190-
onDismissRequest = { onUpdateShowCoinCurrencyBottomSheet(false) }
194+
onDismissRequest = { onUpdateShowCurrencyBottomSheet(false) }
191195
)
192196
}
193197
}
@@ -237,35 +241,19 @@ fun MarketScreen(
237241
@Composable
238242
@OptIn(ExperimentalMaterial3Api::class)
239243
fun MarketTopBar(
240-
onUpdateShowCoinSortBottomSheet: (Boolean) -> Unit,
241-
onUpdateShowCoinCurrencyBottomSheet: (Boolean) -> Unit,
244+
timeOfDay: TimeOfDay,
242245
scrollBehavior: TopAppBarScrollBehavior,
243246
modifier: Modifier = Modifier
244247
) {
245248
TopAppBar(
246249
title = {
247250
Text(
248-
text = stringResource(R.string.market_screen),
251+
text = stringResource(R.string.time_of_day_prefix_good) +
252+
" " + timeOfDay.name.lowercase(),
249253
style = MaterialTheme.typography.titleMedium,
250254
color = MaterialTheme.colorScheme.onBackground
251255
)
252256
},
253-
actions = {
254-
IconButton(onClick = { onUpdateShowCoinCurrencyBottomSheet(true) }) {
255-
Icon(
256-
imageVector = Icons.Rounded.CurrencyExchange,
257-
tint = MaterialTheme.colorScheme.onBackground,
258-
contentDescription = stringResource(R.string.top_bar_action_change_currency)
259-
)
260-
}
261-
IconButton(onClick = { onUpdateShowCoinSortBottomSheet(true) }) {
262-
Icon(
263-
imageVector = Icons.Rounded.SwapVert,
264-
tint = MaterialTheme.colorScheme.onBackground,
265-
contentDescription = stringResource(R.string.top_bar_action_sort_coins)
266-
)
267-
}
268-
},
269257
colors = TopAppBarDefaults.topAppBarColors(
270258
containerColor = MaterialTheme.colorScheme.background,
271259
scrolledContainerColor = MaterialTheme.colorScheme.background
@@ -279,55 +267,75 @@ fun MarketTopBar(
279267
fun MarketContent(
280268
coins: ImmutableList<CachedCoin>,
281269
onCoinClick: (CachedCoin) -> Unit,
270+
currency: Currency,
271+
onUpdateShowCurrencyBottomSheet: (Boolean) -> Unit,
272+
coinSort: CoinSort,
273+
onUpdateShowCoinSortBottomSheet: (Boolean) -> Unit,
282274
lazyListState: LazyListState,
283275
modifier: Modifier = Modifier
284276
) {
285-
if (coins.isEmpty()) {
286-
MarketEmptyState(
287-
modifier = modifier
288-
.fillMaxSize()
289-
.padding(12.dp)
290-
)
291-
} else {
292-
LazyColumn(
293-
state = lazyListState,
294-
contentPadding = PaddingValues(horizontal = 12.dp),
295-
modifier = modifier.fillMaxSize()
296-
) {
297-
// Workaround for https://issuetracker.google.com/issues/209652366
298-
item(key = "0") {
299-
Spacer(Modifier.padding(1.dp))
300-
}
301-
items(
302-
count = coins.size,
303-
key = { coins[it].id },
304-
itemContent = { index ->
305-
val coinListItem = coins[index]
277+
Column(
278+
modifier = modifier
279+
.fillMaxSize()
280+
.padding(horizontal = 12.dp)
281+
) {
282+
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
283+
MarketChip(
284+
label = currency.name.uppercase(),
285+
onClick = { onUpdateShowCurrencyBottomSheet(true) }
286+
)
287+
MarketChip(
288+
label = stringResource(coinSort.nameId),
289+
onClick = { onUpdateShowCoinSortBottomSheet(true) }
290+
)
291+
}
306292

307-
val cardShape = when (index) {
308-
0 -> MaterialTheme.shapes.medium.copy(
309-
bottomStart = CornerSize(0.dp),
310-
bottomEnd = CornerSize(0.dp)
311-
)
293+
if (coins.isEmpty()) {
294+
MarketEmptyState(
295+
modifier = Modifier
296+
.fillMaxSize()
297+
.padding(12.dp)
298+
)
299+
} else {
300+
LazyColumn(
301+
state = lazyListState,
302+
modifier = Modifier.fillMaxSize()
303+
) {
304+
// Workaround for https://issuetracker.google.com/issues/209652366
305+
item(key = "0") {
306+
Spacer(Modifier.padding(1.dp))
307+
}
308+
items(
309+
count = coins.size,
310+
key = { coins[it].id },
311+
itemContent = { index ->
312+
val coinListItem = coins[index]
312313

313-
coins.lastIndex -> MaterialTheme.shapes.medium.copy(
314-
topStart = CornerSize(0.dp),
315-
topEnd = CornerSize(0.dp)
316-
)
314+
val cardShape = when (index) {
315+
0 -> MaterialTheme.shapes.medium.copy(
316+
bottomStart = CornerSize(0.dp),
317+
bottomEnd = CornerSize(0.dp)
318+
)
319+
320+
coins.lastIndex -> MaterialTheme.shapes.medium.copy(
321+
topStart = CornerSize(0.dp),
322+
topEnd = CornerSize(0.dp)
323+
)
324+
325+
else -> RoundedCornerShape(0.dp)
326+
}
317327

318-
else -> RoundedCornerShape(0.dp)
328+
MarketCoinItem(
329+
coin = coinListItem,
330+
onCoinClick = { onCoinClick(coinListItem) },
331+
cardShape = cardShape
332+
)
319333
}
334+
)
320335

321-
MarketCoinItem(
322-
coin = coinListItem,
323-
onCoinClick = { onCoinClick(coinListItem) },
324-
cardShape = cardShape
325-
)
336+
item {
337+
SearchPrompt(modifier = Modifier.padding(vertical = 12.dp))
326338
}
327-
)
328-
329-
item {
330-
SearchPrompt(modifier = Modifier.padding(vertical = 12.dp))
331339
}
332340
}
333341
}
@@ -345,8 +353,8 @@ private fun MarketScreenPreview(
345353
onUpdateCoinSort = {},
346354
onUpdateShowCoinSortBottomSheet = {},
347355
onRefresh = {},
348-
onUpdateCoinCurrency = {},
349-
onUpdateShowCoinCurrencyBottomSheet = {}
356+
onUpdateCurrency = {},
357+
onUpdateShowCurrencyBottomSheet = {}
350358
)
351359
}
352360
}

app/src/main/java/dev/shorthouse/coinwatch/ui/screen/market/MarketUiState.kt

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@ package dev.shorthouse.coinwatch.ui.screen.market
33
import dev.shorthouse.coinwatch.data.datastore.CoinSort
44
import dev.shorthouse.coinwatch.data.datastore.Currency
55
import dev.shorthouse.coinwatch.data.source.local.model.CachedCoin
6+
import dev.shorthouse.coinwatch.ui.model.TimeOfDay
67
import kotlinx.collections.immutable.ImmutableList
78
import kotlinx.collections.immutable.persistentListOf
89

910
data class MarketUiState(
1011
val coins: ImmutableList<CachedCoin> = persistentListOf(),
1112
val coinSort: CoinSort = CoinSort.MarketCap,
1213
val showCoinSortBottomSheet: Boolean = false,
13-
val coinCurrency: Currency = Currency.USD,
14-
val showCoinCurrencyBottomSheet: Boolean = false,
14+
val currency: Currency = Currency.USD,
15+
val showCurrencyBottomSheet: Boolean = false,
1516
val isRefreshing: Boolean = false,
17+
val timeOfDay: TimeOfDay = TimeOfDay.Morning,
1618
val isLoading: Boolean = false,
1719
val errorMessage: String? = null
1820
)

app/src/main/java/dev/shorthouse/coinwatch/ui/screen/market/component/CoinSortBottomSheet.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,12 @@ fun CoinSortBottomSheet(
4848
),
4949
CoinSortOption(
5050
icon = Icons.Rounded.Percent,
51-
labelId = R.string.coin_sort_price_change_24h,
51+
labelId = R.string.coin_sort_price_change,
5252
coinSort = CoinSort.PriceChange24h
5353
),
5454
CoinSortOption(
5555
icon = Icons.Rounded.CurrencyExchange,
56-
labelId = R.string.coin_sort_volume_24h,
56+
labelId = R.string.coin_sort_volume,
5757
coinSort = CoinSort.Volume24h
5858
)
5959
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package dev.shorthouse.coinwatch.ui.screen.market.component
2+
3+
import androidx.compose.material.icons.Icons
4+
import androidx.compose.material.icons.rounded.ExpandMore
5+
import androidx.compose.material3.ExperimentalMaterial3Api
6+
import androidx.compose.material3.FilterChip
7+
import androidx.compose.material3.FilterChipDefaults
8+
import androidx.compose.material3.Icon
9+
import androidx.compose.material3.MaterialTheme
10+
import androidx.compose.material3.Text
11+
import androidx.compose.runtime.Composable
12+
import androidx.compose.ui.Modifier
13+
import androidx.compose.ui.res.stringResource
14+
import dev.shorthouse.coinwatch.R
15+
16+
@OptIn(ExperimentalMaterial3Api::class)
17+
@Composable
18+
fun MarketChip(
19+
label: String,
20+
onClick: () -> Unit,
21+
modifier: Modifier = Modifier
22+
) {
23+
FilterChip(
24+
label = { Text(text = label) },
25+
onClick = onClick,
26+
selected = false,
27+
trailingIcon = {
28+
Icon(
29+
imageVector = Icons.Rounded.ExpandMore,
30+
tint = MaterialTheme.colorScheme.onBackground,
31+
contentDescription = stringResource(
32+
R.string.top_bar_action_change_currency
33+
)
34+
)
35+
},
36+
colors = FilterChipDefaults.filterChipColors(
37+
containerColor = MaterialTheme.colorScheme.surface,
38+
labelColor = MaterialTheme.colorScheme.onSurface,
39+
iconColor = MaterialTheme.colorScheme.onSurface
40+
),
41+
border = null,
42+
modifier = modifier
43+
)
44+
}

0 commit comments

Comments
 (0)