Skip to content

Commit 490977b

Browse files
committed
Migrate filter screen to Viewmodel and StateFlow
1 parent 3b567ca commit 490977b

File tree

7 files changed

+141
-40
lines changed

7 files changed

+141
-40
lines changed

app/src/main/java/in/jitinsharma/asg/conf/di/ConferenceModule.kt

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import `in`.jitinsharma.asg.conf.redux.middleware.ConferenceMiddleware
66
import `in`.jitinsharma.asg.conf.redux.reducer.appReducer
77
import `in`.jitinsharma.asg.conf.repository.ConferenceRepository
88
import `in`.jitinsharma.asg.conf.viewmodel.ConferenceViewModel
9+
import `in`.jitinsharma.asg.conf.viewmodel.FilterScreenViewModel
910
import kotlinx.coroutines.CoroutineScope
1011
import org.koin.android.ext.koin.androidApplication
1112
import org.koin.androidx.viewmodel.dsl.viewModel
@@ -29,4 +30,5 @@ val conferenceModule = module {
2930
}
3031
single { AppPreferences(context = androidApplication()) }
3132
viewModel { ConferenceViewModel(get()) }
33+
viewModel { FilterScreenViewModel(get()) }
3234
}

app/src/main/java/in/jitinsharma/asg/conf/repository/ConferenceRepository.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class ConferenceRepository(
8282
val cfpData = ConferenceData.CfpData()
8383
//TODO Try replacing matchers by parsing line with Jsoup
8484
val cfpUrlMatcher =
85-
Pattern.compile("a href=(.*?)>", Pattern.DOTALL).matcher(line)
85+
Pattern.compile("href=(.*?)>", Pattern.DOTALL).matcher(line)
8686
val cfpDateMatcher = Pattern.compile(">(.*?)<", Pattern.DOTALL).matcher(line)
8787
while (cfpUrlMatcher.find()) {
8888
val match = cfpUrlMatcher.group(1)
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,59 @@
11
package `in`.jitinsharma.asg.conf.ui
22

33
import `in`.jitinsharma.asg.conf.viewmodel.ConferenceViewModel
4+
import `in`.jitinsharma.asg.conf.viewmodel.FilterScreenViewModel
45
import androidx.compose.foundation.background
56
import androidx.compose.foundation.layout.*
67
import androidx.compose.material.MaterialTheme
78
import androidx.compose.runtime.Composable
89
import androidx.compose.runtime.collectAsState
10+
import androidx.compose.runtime.mutableStateOf
11+
import androidx.compose.runtime.remember
912
import androidx.compose.ui.Modifier
1013
import androidx.compose.ui.unit.dp
1114
import kotlinx.coroutines.ExperimentalCoroutinesApi
1215

1316
@ExperimentalCoroutinesApi
1417
@Composable
1518
fun ConferenceApp(
16-
conferenceViewModel: ConferenceViewModel
19+
conferenceViewModel: ConferenceViewModel,
20+
filterScreenViewModel: FilterScreenViewModel
1721
) {
1822
Box(
1923
modifier = Modifier.fillMaxSize().background(color = MaterialTheme.colors.primary)
2024
) {
2125
Column(modifier = Modifier.fillMaxWidth().padding(all = 16.dp)) {
26+
val filterScreenDialogState = remember { mutableStateOf(false) }
2227
Header(
2328
modifier = Modifier.padding(bottom = 16.dp),
24-
onFilterClicked = {},
29+
onFilterClicked = {
30+
filterScreenDialogState.value = filterScreenDialogState.value.not()
31+
},
2532
onSettingsClicked = {}
2633
)
34+
2735
val conferenceUiState = conferenceViewModel.uiState.collectAsState()
2836
ConferencePage(
2937
conferenceListUiState = conferenceUiState.value,
3038
onRetryClick = {
3139
conferenceViewModel.loadConferences()
3240
}
3341
)
42+
43+
if (filterScreenDialogState.value) {
44+
val filterScreenUiState = filterScreenViewModel.uiState.collectAsState()
45+
FilterDialog(
46+
filterScreenUiState = filterScreenUiState.value,
47+
onDismissRequest = {
48+
filterScreenDialogState.value = false
49+
},
50+
onFilterRequest = { cfpFilterChecked, selectedCountries ->
51+
conferenceViewModel.filterConferences(cfpFilterChecked, selectedCountries)
52+
filterScreenViewModel.updateUiState(cfpFilterChecked, selectedCountries)
53+
filterScreenDialogState.value = false
54+
}
55+
)
56+
}
3457
}
3558
}
3659
}

app/src/main/java/in/jitinsharma/asg/conf/ui/FiltersScreen.kt

+36-32
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package `in`.jitinsharma.asg.conf.ui
22

33
import `in`.jitinsharma.asg.conf.model.Country
4-
import `in`.jitinsharma.asg.conf.redux.actions.*
5-
import `in`.jitinsharma.asg.conf.redux.state.AppState
6-
import `in`.jitinsharma.asg.conf.redux.state.FilterState
74
import `in`.jitinsharma.asg.conf.utils.ThemedPreview
5+
import `in`.jitinsharma.asg.conf.viewmodel.FilterScreenUiState
86
import androidx.compose.foundation.background
97
import androidx.compose.foundation.clickable
108
import androidx.compose.foundation.layout.*
@@ -22,33 +20,41 @@ import androidx.compose.ui.text.font.FontWeight
2220
import androidx.compose.ui.tooling.preview.Preview
2321
import androidx.compose.ui.unit.dp
2422
import androidx.compose.ui.window.Dialog
25-
import org.koin.java.KoinJavaComponent.getKoin
26-
import org.rekotlin.Store
2723

2824
@Composable
2925
fun FilterDialog(
30-
filterState: FilterState
26+
filterScreenUiState: FilterScreenUiState,
27+
onDismissRequest: () -> Unit,
28+
onFilterRequest: (cfpFilterChecked: Boolean, selectedCountries: List<Country>) -> Unit
3129
) {
32-
if (filterState.displayDialog) {
33-
val store = remember { getKoin().get<Store<AppState>>() }
34-
store.dispatch(LoadCountries())
35-
Dialog(onDismissRequest = { store.dispatch(HideDialog()) }) {
36-
FiltersScreen(
37-
cfpFilterChecked = filterState.cfpFilterChecked,
38-
selectedCountries = filterState.selectedCountries,
39-
countyList = filterState.countryList
40-
)
30+
//if (filterState.displayDialog) {
31+
//val store = remember { getKoin().get<Store<AppState>>() }
32+
//store.dispatch(LoadCountries())
33+
Dialog(onDismissRequest = { onDismissRequest() }) {
34+
when (filterScreenUiState) {
35+
is FilterScreenUiState.Success -> {
36+
FiltersScreen(
37+
cfpFilterChecked = filterScreenUiState.cfpFilterChecked,
38+
selectedCountries = filterScreenUiState.selectedCountries.toMutableList(),
39+
countyList = filterScreenUiState.countryList,
40+
onDismiss = onDismissRequest,
41+
onApply = onFilterRequest
42+
)
43+
}
4144
}
4245
}
46+
//}
4347
}
4448

4549
@Composable
4650
fun FiltersScreen(
4751
cfpFilterChecked: Boolean = false,
4852
selectedCountries: MutableList<Country>,
49-
countyList: List<Country>?
53+
countyList: List<Country>?,
54+
onDismiss: () -> Unit,
55+
onApply: (cfpFilterChecked: Boolean, selectedCountries: List<Country>) -> Unit
5056
) {
51-
val store = remember { getKoin().get<Store<AppState>>() }
57+
//val store = remember { getKoin().get<Store<AppState>>() }
5258
Card(backgroundColor = themeColors.secondary) {
5359
Column(modifier = Modifier.wrapContentSize()) {
5460
Box(
@@ -94,7 +100,10 @@ fun FiltersScreen(
94100
onCheckedChange = {
95101
cfpFilterCheckState.value = cfpFilterCheckState.value.not()
96102
},
97-
colors = CheckboxConstants.defaultColors(checkedColor = themeColors.primary)
103+
colors = CheckboxDefaults.colors(
104+
checkedColor = MaterialTheme.colors.primary,
105+
uncheckedColor = MaterialTheme.colors.primary
106+
)
98107
)
99108
Text(
100109
text = "Cfp Open",
@@ -124,7 +133,7 @@ fun FiltersScreen(
124133
Box(
125134
Modifier.clickable(
126135
indication = rememberRippleIndication(),
127-
onClick = { store.dispatch(HideDialog()) })
136+
onClick = { onDismiss() })
128137
) {
129138
Text(
130139
text = "CANCEL",
@@ -140,17 +149,7 @@ fun FiltersScreen(
140149
Box(
141150
Modifier.clickable(
142151
indication = rememberRippleIndication(),
143-
onClick = {
144-
store.dispatch(SetCFPFilterCheck(cfpFilterCheckState.value))
145-
store.dispatch(SetSelectedCountries(selectedCountries))
146-
store.dispatch(HideDialog())
147-
store.dispatch(
148-
FilterConferences(
149-
cfpFilterCheckState.value,
150-
selectedCountries
151-
)
152-
)
153-
})
152+
onClick = { onApply(cfpFilterCheckState.value, selectedCountries) })
154153
) {
155154
Text(
156155
text = "APPLY",
@@ -217,7 +216,10 @@ fun CountryList(
217216
selectedCountries.remove(country)
218217
}
219218
},
220-
colors = CheckboxConstants.defaultColors(checkedColor = themeColors.primary)
219+
colors = CheckboxDefaults.colors(
220+
checkedColor = MaterialTheme.colors.primary,
221+
uncheckedColor = MaterialTheme.colors.primary
222+
)
221223
)
222224
Text(
223225
text = country.name,
@@ -245,7 +247,9 @@ fun FilterScreenPreview() {
245247
Country("Japan"),
246248
Country("Poland")
247249
),
248-
selectedCountries = mutableListOf()
250+
selectedCountries = mutableListOf(),
251+
onDismiss = {},
252+
onApply = { _, _ -> }
249253
)
250254
}
251255
}

app/src/main/java/in/jitinsharma/asg/conf/ui/MainActivity.kt

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,28 @@
11
package `in`.jitinsharma.asg.conf.ui
22

33
import `in`.jitinsharma.asg.conf.viewmodel.ConferenceViewModel
4+
import `in`.jitinsharma.asg.conf.viewmodel.FilterScreenViewModel
45
import android.os.Bundle
56
import androidx.appcompat.app.AppCompatActivity
67
import androidx.compose.material.MaterialTheme
78
import androidx.compose.ui.platform.setContent
8-
import androidx.lifecycle.lifecycleScope
99
import kotlinx.coroutines.ExperimentalCoroutinesApi
1010
import org.koin.androidx.viewmodel.ext.android.viewModel
1111

1212
@ExperimentalCoroutinesApi
1313
class MainActivity : AppCompatActivity() {
1414

1515
private val conferenceViewModel by viewModel<ConferenceViewModel>()
16+
private val filterScreenViewModel by viewModel<FilterScreenViewModel>()
1617

1718
override fun onCreate(savedInstanceState: Bundle?) {
1819
super.onCreate(savedInstanceState)
1920
setContent {
2021
MaterialTheme(colors = themeColors) {
21-
lifecycleScope.launchWhenStarted {
22-
conferenceViewModel.loadConferences()
23-
}
24-
ConferenceApp(conferenceViewModel = conferenceViewModel)
22+
ConferenceApp(
23+
conferenceViewModel = conferenceViewModel,
24+
filterScreenViewModel = filterScreenViewModel
25+
)
2526
}
2627
}
2728
}

app/src/main/java/in/jitinsharma/asg/conf/viewmodel/ConferenceViewModel.kt

+24
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package `in`.jitinsharma.asg.conf.viewmodel
22

33
import `in`.jitinsharma.asg.conf.model.ConferenceData
4+
import `in`.jitinsharma.asg.conf.model.Country
45
import `in`.jitinsharma.asg.conf.repository.ConferenceRepository
56
import androidx.lifecycle.ViewModel
67
import androidx.lifecycle.viewModelScope
@@ -16,18 +17,41 @@ class ConferenceViewModel(
1617
private var _uiState: MutableStateFlow<ConferenceListUiState> =
1718
MutableStateFlow(ConferenceListUiState.Loading)
1819
val uiState: StateFlow<ConferenceListUiState> = _uiState
20+
private var originalConferenceList = listOf<ConferenceData>()
21+
22+
init {
23+
loadConferences()
24+
}
1925

2026
fun loadConferences() {
2127
viewModelScope.launch(Dispatchers.IO) {
2228
conferenceRepository.loadConferenceData()
2329
val conferenceDataList = conferenceRepository.getConferenceDataList()
2430
if (conferenceDataList.isNotEmpty()) {
2531
_uiState.value = ConferenceListUiState.Success(conferenceDataList)
32+
originalConferenceList = conferenceDataList
2633
} else {
2734
_uiState.value = ConferenceListUiState.Error
2835
}
2936
}
3037
}
38+
39+
fun filterConferences(cfpFilterChecked: Boolean, selectedCountries: List<Country>) {
40+
if (!cfpFilterChecked && selectedCountries.isEmpty()) {
41+
_uiState.value = ConferenceListUiState.Success(originalConferenceList)
42+
} else {
43+
val filteredConferences = originalConferenceList.filter {
44+
if (cfpFilterChecked) {
45+
it.cfpData != null && it.cfpData!!.isCfpActive
46+
} else {
47+
true
48+
}
49+
}.filter {
50+
selectedCountries.contains(Country(it.country))
51+
}
52+
_uiState.value = ConferenceListUiState.Success(filteredConferences)
53+
}
54+
}
3155
}
3256

3357
sealed class ConferenceListUiState {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package `in`.jitinsharma.asg.conf.viewmodel
2+
3+
import `in`.jitinsharma.asg.conf.model.Country
4+
import `in`.jitinsharma.asg.conf.repository.ConferenceRepository
5+
import androidx.collection.ArraySet
6+
import androidx.lifecycle.ViewModel
7+
import androidx.lifecycle.viewModelScope
8+
import kotlinx.coroutines.Dispatchers
9+
import kotlinx.coroutines.flow.MutableStateFlow
10+
import kotlinx.coroutines.flow.StateFlow
11+
import kotlinx.coroutines.launch
12+
13+
class FilterScreenViewModel(
14+
private val conferenceRepository: ConferenceRepository
15+
) : ViewModel() {
16+
17+
private var _uiState =
18+
MutableStateFlow(FilterScreenUiState.Success())
19+
val uiState: StateFlow<FilterScreenUiState> = _uiState
20+
21+
init {
22+
loadCountries()
23+
}
24+
25+
fun loadCountries() {
26+
viewModelScope.launch(Dispatchers.IO) {
27+
val conferenceDataList = conferenceRepository.getConferenceDataList()
28+
val countries = conferenceDataList.mapTo(ArraySet()) { countryName ->
29+
Country(name = countryName.country)
30+
}.toList()
31+
_uiState.value = FilterScreenUiState.Success(countryList = countries)
32+
}
33+
}
34+
35+
fun updateUiState(cfpFilterChecked: Boolean, selectedCountries: List<Country>) {
36+
val countries = _uiState.value.countryList
37+
_uiState.value = FilterScreenUiState.Success(cfpFilterChecked, selectedCountries, countries)
38+
}
39+
}
40+
41+
sealed class FilterScreenUiState {
42+
class Success(
43+
val cfpFilterChecked: Boolean = false,
44+
val selectedCountries: List<Country> = emptyList(),
45+
val countryList: List<Country> = emptyList()
46+
) : FilterScreenUiState()
47+
}

0 commit comments

Comments
 (0)