Skip to content

Commit 40ffa94

Browse files
committed
Initial use of Molecule
1 parent f3d4bbd commit 40ffa94

File tree

5 files changed

+80
-44
lines changed

5 files changed

+80
-44
lines changed

composeApp/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ kotlin {
6464
implementation(compose.components.resources)
6565
implementation(compose.components.uiToolingPreview)
6666

67+
implementation(libs.molecule)
6768

6869
implementation(libs.koin.core)
6970
implementation(libs.koin.compose)

composeApp/src/commonMain/kotlin/dev/johnoreilly/climatetrace/ui/ClimateTraceScreen.kt

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import androidx.compose.runtime.collectAsState
4242
import androidx.compose.runtime.getValue
4343
import androidx.compose.runtime.mutableStateOf
4444
import androidx.compose.runtime.remember
45+
import androidx.compose.runtime.setValue
4546
import androidx.compose.ui.Alignment
4647
import androidx.compose.ui.Modifier
4748
import androidx.compose.ui.graphics.Color
@@ -66,9 +67,7 @@ class ClimateTraceScreen: Screen {
6667
Column(Modifier) {
6768
when (val state = countryListViewState) {
6869
is CountryListUIState.Loading -> {
69-
Column(modifier = Modifier.fillMaxSize().fillMaxHeight()
70-
.wrapContentSize(Alignment.Center)
71-
) {
70+
Column(modifier = Modifier.fillMaxSize().fillMaxHeight().wrapContentSize(Alignment.Center)) {
7271
CircularProgressIndicator()
7372
}
7473
}
@@ -88,10 +87,8 @@ class ClimateTraceScreen: Screen {
8887
fun CountryScreenSuccess(countryList: List<Country>) {
8988
val windowSizeClass = calculateWindowSizeClass()
9089
val countryDetailsViewModel = koinInject<CountryDetailsViewModel>()
91-
92-
93-
val selectedCountry by countryDetailsViewModel.selectedCountry.collectAsState()
9490
val countryDetailsViewState by countryDetailsViewModel.viewState.collectAsState()
91+
var selectedCountry by remember { mutableStateOf<Country?>(null) }
9592

9693
val panelState = remember { PanelState() }
9794

@@ -108,14 +105,14 @@ fun CountryScreenSuccess(countryList: List<Country>) {
108105
Row(Modifier.fillMaxSize()) {
109106
if (windowSizeClass.widthSizeClass == WindowWidthSizeClass.Compact) {
110107
Column(Modifier.fillMaxWidth()) {
111-
112108
Box(
113109
Modifier.height(250.dp).fillMaxWidth().background(color = Color.LightGray)
114110
) {
115111
CountryListView(
116112
countryList = countryList,
117113
selectedCountry = selectedCountry,
118114
) { country ->
115+
selectedCountry = country
119116
countryDetailsViewModel.setCountry(country)
120117
}
121118
}
@@ -136,6 +133,7 @@ fun CountryScreenSuccess(countryList: List<Country>) {
136133
countryList = countryList,
137134
selectedCountry = selectedCountry,
138135
) { country ->
136+
selectedCountry = country
139137
countryDetailsViewModel.setCountry(country)
140138
}
141139
}

composeApp/src/commonMain/kotlin/dev/johnoreilly/climatetrace/ui/CountryEmissionsScreen.kt

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,18 @@ data class CountryEmissionsScreen(val country: Country) : Screen {
3232
}
3333

3434
Scaffold(
35-
topBar = {
36-
CenterAlignedTopAppBar(
37-
title = {
38-
Text("ClimateTraceKMP")
39-
},
40-
navigationIcon = {
41-
IconButton(onClick = { navigator.pop() }) {
42-
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
43-
}
44-
}
45-
)
46-
}
35+
topBar = {
36+
CenterAlignedTopAppBar(
37+
title = {
38+
Text("ClimateTraceKMP")
39+
},
40+
navigationIcon = {
41+
IconButton(onClick = { navigator.pop() }) {
42+
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
43+
}
44+
}
45+
)
46+
}
4747
) {
4848
Column(Modifier.padding(it)) {
4949
CountryInfoDetailedView(viewState) { year ->
Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
package dev.johnoreilly.climatetrace.viewmodel
22

3-
import com.rickclephas.kmp.nativecoroutines.NativeCoroutinesState
4-
import com.rickclephas.kmp.observableviewmodel.MutableStateFlow
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.LaunchedEffect
5+
import androidx.compose.runtime.getValue
6+
import androidx.compose.runtime.mutableStateOf
7+
import androidx.compose.runtime.remember
8+
import androidx.compose.runtime.setValue
9+
import app.cash.molecule.RecompositionMode
10+
import app.cash.molecule.launchMolecule
511
import com.rickclephas.kmp.observableviewmodel.ViewModel
612
import com.rickclephas.kmp.observableviewmodel.coroutineScope
13+
import com.rickclephas.kmp.observableviewmodel.launch
714
import dev.johnoreilly.climatetrace.data.ClimateTraceRepository
815
import dev.johnoreilly.climatetrace.remote.Country
916
import dev.johnoreilly.climatetrace.remote.CountryAssetEmissionsInfo
1017
import dev.johnoreilly.climatetrace.remote.CountryEmissionsInfo
11-
import kotlinx.coroutines.flow.asStateFlow
12-
import kotlinx.coroutines.launch
18+
import kotlinx.coroutines.flow.Flow
19+
import kotlinx.coroutines.flow.MutableSharedFlow
20+
import kotlinx.coroutines.flow.StateFlow
1321
import org.koin.core.component.KoinComponent
1422
import org.koin.core.component.inject
1523

1624

17-
1825
sealed class CountryDetailsUIState {
1926
object NoCountrySelected : CountryDetailsUIState()
2027
object Loading : CountryDetailsUIState()
@@ -27,39 +34,66 @@ sealed class CountryDetailsUIState {
2734
) : CountryDetailsUIState()
2835
}
2936

37+
sealed interface CountryDetailsEvents {
38+
data class SetCountry(val country: Country): CountryDetailsEvents
39+
data class SetYear(val year: String): CountryDetailsEvents
40+
}
3041

3142
open class CountryDetailsViewModel : ViewModel(), KoinComponent {
3243
private val climateTraceRepository: ClimateTraceRepository by inject()
3344

34-
private val _viewState = MutableStateFlow<CountryDetailsUIState>(viewModelScope, CountryDetailsUIState.NoCountrySelected)
35-
@NativeCoroutinesState
36-
val viewState = _viewState.asStateFlow()
37-
45+
private val events = MutableSharedFlow<CountryDetailsEvents>()
3846

39-
@NativeCoroutinesState
40-
val selectedYear = MutableStateFlow<String>(viewModelScope, "2022")
41-
42-
@NativeCoroutinesState
43-
val selectedCountry = MutableStateFlow<Country?>(viewModelScope, null)
47+
val viewState: StateFlow<CountryDetailsUIState> = viewModelScope.coroutineScope.launchMolecule(mode = RecompositionMode.Immediate) {
48+
CountryDetailsPresenter(events)
49+
}
4450

4551
fun setYear(year: String) {
46-
selectedYear.value = year
47-
fetchCountryDetails()
52+
viewModelScope.launch {
53+
events.emit(CountryDetailsEvents.SetYear(year))
54+
}
4855
}
4956

5057
fun setCountry(country: Country) {
51-
selectedCountry.value = country
52-
fetchCountryDetails()
58+
viewModelScope.launch {
59+
events.emit(CountryDetailsEvents.SetCountry(country))
60+
}
5361
}
5462

55-
fun fetchCountryDetails() {
56-
selectedCountry.value?.let { country ->
57-
_viewState.value = CountryDetailsUIState.Loading
58-
viewModelScope.coroutineScope.launch {
59-
val countryEmissionInfo = climateTraceRepository.fetchCountryEmissionsInfo(country.alpha3, selectedYear.value).firstOrNull()
60-
val countryAssetEmissionsList = climateTraceRepository.fetchCountryAssetEmissionsInfo(country.alpha3)
61-
_viewState.value = CountryDetailsUIState.Success(country, selectedYear.value, countryEmissionInfo, countryAssetEmissionsList)
63+
@Composable
64+
fun CountryDetailsPresenter(events: Flow<CountryDetailsEvents>): CountryDetailsUIState {
65+
var uiState by remember { mutableStateOf<CountryDetailsUIState>(CountryDetailsUIState.NoCountrySelected) }
66+
var selectedCountry by remember { mutableStateOf<Country?>(null) }
67+
var selectedYear by remember { mutableStateOf<String>("2022") }
68+
69+
var countryEmissionInfo by remember { mutableStateOf<CountryEmissionsInfo?>(null) }
70+
var countryAssetEmissionsList by remember { mutableStateOf<List<CountryAssetEmissionsInfo>>(emptyList()) }
71+
72+
LaunchedEffect(Unit) {
73+
events.collect { event ->
74+
when (event) {
75+
is CountryDetailsEvents.SetCountry -> {
76+
selectedCountry = event.country
77+
selectedCountry?.let { country ->
78+
uiState = CountryDetailsUIState.Loading
79+
countryEmissionInfo = climateTraceRepository.fetchCountryEmissionsInfo(country.alpha3, selectedYear).firstOrNull()
80+
countryAssetEmissionsList = climateTraceRepository.fetchCountryAssetEmissionsInfo(country.alpha3)
81+
uiState = CountryDetailsUIState.Success(country, selectedYear, countryEmissionInfo, countryAssetEmissionsList)
82+
}
83+
}
84+
85+
is CountryDetailsEvents.SetYear -> {
86+
selectedCountry?.let { country ->
87+
selectedYear = event.year
88+
countryEmissionInfo = climateTraceRepository.fetchCountryEmissionsInfo(country.alpha3, selectedYear).firstOrNull()
89+
uiState = CountryDetailsUIState.Success(country, selectedYear, countryEmissionInfo, countryAssetEmissionsList)
90+
}
91+
}
92+
}
6293
}
6394
}
95+
96+
return uiState
6497
}
6598
}
99+

gradle/libs.versions.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ kstore = "0.8.0"
2323
ktor = "3.0.0-wasm2"
2424
treemapChart = "0.1.1"
2525
voyager= "1.1.0-beta02"
26+
molecule = "2.0.0"
27+
2628

2729

2830
[libraries]
@@ -60,6 +62,7 @@ koalaplot = { module = "io.github.koalaplot:koalaplot-core", version.ref = "koal
6062
treemap-chart = { module = "io.github.overpas:treemap-chart", version.ref = "treemapChart" }
6163
treemap-chart-compose = { module = "io.github.overpas:treemap-chart-compose", version.ref = "treemapChart" }
6264

65+
molecule = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" }
6366

6467
[bundles]
6568
ktor-common = ["ktor-client-core", "ktor-client-json", "ktor-client-logging", "ktor-client-serialization", "ktor-client-content-negotiation", "ktor-serialization-kotlinx-json"]

0 commit comments

Comments
 (0)