Skip to content

Commit 0c4eca7

Browse files
authored
feat(filter): adds filtering for set (#4)
1 parent e747613 commit 0c4eca7

File tree

8 files changed

+137
-11
lines changed

8 files changed

+137
-11
lines changed

.idea/inspectionProfiles/Project_Default.xml

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ dependencies {
5555
implementation(libs.androidx.compose.google.fonts)
5656
implementation(libs.androidx.compose.navigation)
5757
implementation(libs.androidx.core.ktx)
58+
implementation(libs.androidx.material.icons)
5859
implementation(libs.androidx.material3)
5960
implementation(libs.androidx.ui)
6061
implementation(libs.androidx.ui.graphics)

app/src/main/java/us/kikin/android/ptp/cards/CardsFilterType.kt

+11-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@
1616

1717
package us.kikin.android.ptp.cards
1818

19-
enum class CardsFilterType {
20-
ALL_CARDS,
19+
import androidx.annotation.StringRes
20+
import us.kikin.android.ptp.R
21+
22+
enum class CardsFilterType(
23+
val setId: String,
24+
@StringRes val titleResId: Int,
25+
) {
26+
All("All", R.string.card_list_filter_all),
27+
GeneticApex("Genetic Apex (A1)", R.string.card_list_filter_genetic_apex),
28+
PromoA("Promo-A", R.string.card_list_filter_promo_a),
29+
MythicalIslands("Mythical Island (A1a)", R.string.card_list_filter_mythical_islands),
2130
}

app/src/main/java/us/kikin/android/ptp/cards/CardsScreen.kt

+84-1
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,37 @@ import androidx.compose.foundation.layout.padding
2727
import androidx.compose.foundation.lazy.grid.GridCells
2828
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
2929
import androidx.compose.foundation.lazy.grid.items
30+
import androidx.compose.foundation.text.input.TextFieldLineLimits
31+
import androidx.compose.foundation.text.input.rememberTextFieldState
32+
import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
3033
import androidx.compose.material3.Card as MaterialCard
3134
import androidx.compose.material3.CenterAlignedTopAppBar
3235
import androidx.compose.material3.CircularProgressIndicator
36+
import androidx.compose.material3.DropdownMenuItem
3337
import androidx.compose.material3.ExperimentalMaterial3Api
38+
import androidx.compose.material3.ExposedDropdownMenuAnchorType
39+
import androidx.compose.material3.ExposedDropdownMenuBox
40+
import androidx.compose.material3.ExposedDropdownMenuDefaults
3441
import androidx.compose.material3.Icon
3542
import androidx.compose.material3.IconButton
3643
import androidx.compose.material3.MaterialTheme
44+
import androidx.compose.material3.ModalBottomSheet
3745
import androidx.compose.material3.Scaffold
3846
import androidx.compose.material3.SnackbarHost
3947
import androidx.compose.material3.SnackbarHostState
4048
import androidx.compose.material3.Text
49+
import androidx.compose.material3.TextField
4150
import androidx.compose.material3.TopAppBarDefaults
51+
import androidx.compose.material3.rememberModalBottomSheetState
4252
import androidx.compose.material3.rememberTopAppBarState
4353
import androidx.compose.runtime.Composable
4454
import androidx.compose.runtime.CompositionLocalProvider
4555
import androidx.compose.runtime.LaunchedEffect
4656
import androidx.compose.runtime.getValue
57+
import androidx.compose.runtime.mutableStateOf
4758
import androidx.compose.runtime.remember
59+
import androidx.compose.runtime.rememberCoroutineScope
60+
import androidx.compose.runtime.setValue
4861
import androidx.compose.ui.Modifier
4962
import androidx.compose.ui.graphics.Color
5063
import androidx.compose.ui.input.nestedscroll.nestedScroll
@@ -78,6 +91,9 @@ fun CardsScreen(
7891
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() },
7992
) {
8093
val scaffoldBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
94+
val sheetState = rememberModalBottomSheetState()
95+
val scope = rememberCoroutineScope()
96+
var showBottomSheet by remember { mutableStateOf(false) }
8197
AppTheme {
8298
Scaffold(
8399
modifier = modifier
@@ -94,7 +110,7 @@ fun CardsScreen(
94110
)
95111
},
96112
actions = {
97-
IconButton(onClick = { /*TODO*/ }) {
113+
IconButton(onClick = { showBottomSheet = true }) {
98114
Icon(
99115
imageVector = PtpIcons.Rounded.FilterList,
100116
contentDescription = stringResource(R.string.card_list_filters),
@@ -123,6 +139,20 @@ fun CardsScreen(
123139
viewModel.snackbarMessageShown()
124140
}
125141
}
142+
143+
if (showBottomSheet) {
144+
ModalBottomSheet(
145+
onDismissRequest = {
146+
showBottomSheet = false
147+
},
148+
sheetState = sheetState,
149+
) {
150+
FilterContent(
151+
currentFilter = uiState.filter,
152+
viewModel::setFiltering,
153+
)
154+
}
155+
}
126156
}
127157
}
128158
}
@@ -149,6 +179,59 @@ private fun CardsContent(
149179
}
150180
}
151181

182+
@OptIn(ExperimentalMaterial3Api::class)
183+
@Composable
184+
fun FilterContent(
185+
currentFilter: CardsFilterType,
186+
onFilterChange: (CardsFilterType) -> Unit,
187+
modifier: Modifier = Modifier,
188+
) {
189+
Column(
190+
modifier = modifier
191+
.fillMaxWidth()
192+
.padding(horizontal = 16.dp, vertical = 24.dp),
193+
) {
194+
// dropdown for set?
195+
val options = CardsFilterType.entries
196+
var expanded by remember { mutableStateOf(false) }
197+
val textFieldState = rememberTextFieldState(currentFilter.name)
198+
ExposedDropdownMenuBox(
199+
expanded = expanded,
200+
onExpandedChange = { expanded = it },
201+
modifier = Modifier.fillMaxWidth(),
202+
) {
203+
TextField(
204+
modifier = Modifier
205+
.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable)
206+
.fillMaxWidth(),
207+
state = textFieldState,
208+
readOnly = true,
209+
lineLimits = TextFieldLineLimits.SingleLine,
210+
label = {
211+
Text("Set")
212+
},
213+
)
214+
ExposedDropdownMenu(
215+
expanded = expanded,
216+
onDismissRequest = { expanded = false },
217+
) {
218+
options.forEach { option ->
219+
val label = stringResource(option.titleResId)
220+
DropdownMenuItem(
221+
text = { Text(label, style = MaterialTheme.typography.bodyLarge) },
222+
onClick = {
223+
textFieldState.setTextAndPlaceCursorAtEnd(label)
224+
expanded = false
225+
onFilterChange(option)
226+
},
227+
contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
228+
)
229+
}
230+
}
231+
}
232+
}
233+
}
234+
152235
@Composable
153236
private fun CardsList(
154237
cards: ImmutableList<Card>,

app/src/main/java/us/kikin/android/ptp/cards/CardsViewModel.kt

+22-5
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package us.kikin.android.ptp.cards
1919
import androidx.lifecycle.SavedStateHandle
2020
import androidx.lifecycle.ViewModel
2121
import androidx.lifecycle.viewModelScope
22-
import androidx.navigation.toRoute
2322
import dagger.hilt.android.lifecycle.HiltViewModel
2423
import javax.inject.Inject
2524
import kotlinx.coroutines.flow.MutableStateFlow
@@ -31,13 +30,13 @@ import kotlinx.coroutines.flow.stateIn
3130
import us.kikin.android.ptp.R
3231
import us.kikin.android.ptp.data.Card
3332
import us.kikin.android.ptp.data.CardRepository
34-
import us.kikin.android.ptp.navigation.CardListDestination
3533
import us.kikin.android.ptp.util.Async
3634
import us.kikin.android.ptp.util.WhileUiSubscribed
3735

3836
data class CardsUiState(
3937
val cards: List<Card> = emptyList(),
4038
val isLoading: Boolean = false,
39+
val filter: CardsFilterType = CardsFilterType.All,
4140
val userMessage: Int? = null,
4241
)
4342

@@ -49,7 +48,8 @@ constructor(
4948
private val savedStateHandle: SavedStateHandle,
5049
) : ViewModel() {
5150
private val _savedFilterType =
52-
MutableStateFlow(savedStateHandle.toRoute<CardListDestination>().filterType)
51+
savedStateHandle.getStateFlow(CARDS_FILTER_SAVED_STATE_KEY, CardsFilterType.All)
52+
private val _filterUiInfo = _savedFilterType
5353
private val _isLoading = MutableStateFlow(false)
5454
private val _userMessage: MutableStateFlow<Int?> = MutableStateFlow(null)
5555
private val _filteredCardsAsync =
@@ -61,10 +61,11 @@ constructor(
6161

6262
val uiState: StateFlow<CardsUiState> =
6363
combine(
64+
_filterUiInfo,
6465
_isLoading,
6566
_userMessage,
6667
_filteredCardsAsync,
67-
) { isLoading, userMessage, cardsAsync ->
68+
) { filterUiInfo, isLoading, userMessage, cardsAsync ->
6869
when (cardsAsync) {
6970
Async.Loading -> {
7071
CardsUiState(isLoading = true)
@@ -77,6 +78,7 @@ constructor(
7778
is Async.Success -> {
7879
CardsUiState(
7980
cards = cardsAsync.data,
81+
filter = filterUiInfo,
8082
isLoading = isLoading,
8183
userMessage = userMessage,
8284
)
@@ -91,11 +93,26 @@ constructor(
9193

9294
private fun filterCards(cards: List<Card>, type: CardsFilterType): List<Card> {
9395
return when (type) {
94-
CardsFilterType.ALL_CARDS -> cards
96+
CardsFilterType.All -> cards
97+
CardsFilterType.GeneticApex -> cards.filter {
98+
it.setDetails == CardsFilterType.GeneticApex.setId
99+
}
100+
CardsFilterType.PromoA -> cards.filter {
101+
it.setDetails == CardsFilterType.PromoA.setId
102+
}
103+
CardsFilterType.MythicalIslands -> cards.filter {
104+
it.setDetails == CardsFilterType.MythicalIslands.setId
105+
}
95106
}
96107
}
97108

98109
fun snackbarMessageShown() {
99110
_userMessage.value = null
100111
}
112+
113+
fun setFiltering(requestType: CardsFilterType) {
114+
savedStateHandle[CARDS_FILTER_SAVED_STATE_KEY] = requestType
115+
}
101116
}
117+
118+
const val CARDS_FILTER_SAVED_STATE_KEY = "CARDS_FILTER_SAVED_STATE_KEY"

app/src/main/java/us/kikin/android/ptp/navigation/AppNavigation.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import us.kikin.android.ptp.cards.CardsFilterType
2121

2222
@Serializable
2323
data class CardListDestination(
24-
val filterType: CardsFilterType = CardsFilterType.ALL_CARDS,
24+
val filterType: CardsFilterType = CardsFilterType.All,
2525
val userMessage: Int? = null,
2626
)
2727

app/src/main/res/values/strings.xml

+4
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,8 @@
55
<string name="loading_card_detail_error">Error while loading card detail</string>
66
<string name="card_detail_not_found">Card not found</string>
77
<string name="card_list_filters">Filters</string>
8+
<string name="card_list_filter_all">All</string>
9+
<string name="card_list_filter_genetic_apex">Genetic Apex</string>
10+
<string name="card_list_filter_promo_a">Promo-A</string>
11+
<string name="card_list_filter_mythical_islands">Mythical Islands</string>
812
</resources>

gradle/libs.versions.toml

+4-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ junitVersion = "1.2.1"
1313
kotlin = "2.1.0"
1414
ksp = "2.1.0-1.0.29"
1515
lifecycle = "2.8.7"
16+
material3 = "1.4.0-alpha05"
1617
paging = "3.3.5"
1718
room = "2.6.1"
1819
timber = "5.0.1"
@@ -26,7 +27,8 @@ androidx-compose-google-fonts = { group = "androidx.compose.ui", name = "ui-text
2627
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
2728
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
2829
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
29-
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
30+
androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
31+
androidx-material-icons = { group = "androidx.compose.material", name = "material-icons-core" }
3032
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
3133
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
3234
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
@@ -58,7 +60,7 @@ room = ["room-runtime", "room-ktx", "room-paging"]
5860

5961
[plugins]
6062
android-application = { id = "com.android.application", version.ref = "agp" }
61-
dependency-sorter = { id = "com.squareup.sort-dependencies", version = "0.13"}
63+
dependency-sorter = { id = "com.squareup.sort-dependencies", version = "0.13" }
6264
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
6365
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
6466
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }

0 commit comments

Comments
 (0)