Skip to content

Commit f3d4bbd

Browse files
authored
Merge pull request #91 from joreilly/viewmodel_updates
view model updates
2 parents be92201 + c6c6238 commit f3d4bbd

File tree

11 files changed

+294
-219
lines changed

11 files changed

+294
-219
lines changed

composeApp/src/commonMain/kotlin/dev/johnoreilly/climatetrace/data/ClimateTraceRepository.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import dev.johnoreilly.climatetrace.remote.ClimateTraceApi
44
import dev.johnoreilly.climatetrace.remote.Country
55
import io.github.xxfast.kstore.KStore
66

7+
78
class ClimateTraceRepository(
89
private val store: KStore<List<Country>>,
910
private val api: ClimateTraceApi
@@ -13,4 +14,7 @@ class ClimateTraceRepository(
1314
if (countries.isNullOrEmpty()) return api.fetchCountries().also { store.set(it) }
1415
return countries
1516
}
17+
18+
suspend fun fetchCountryEmissionsInfo(countryCode: String, year: String) = api.fetchCountryEmissionsInfo(countryCode, year)
19+
suspend fun fetchCountryAssetEmissionsInfo(countryCode: String) = api.fetchCountryAssetEmissionsInfo(countryCode)
1620
}

composeApp/src/commonMain/kotlin/dev/johnoreilly/climatetrace/di/Koin.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ package dev.johnoreilly.climatetrace.di
22

33
import dev.johnoreilly.climatetrace.data.ClimateTraceRepository
44
import dev.johnoreilly.climatetrace.remote.ClimateTraceApi
5-
import dev.johnoreilly.climatetrace.viewmodel.ClimateTraceViewModel
5+
import dev.johnoreilly.climatetrace.viewmodel.CountryDetailsViewModel
6+
import dev.johnoreilly.climatetrace.viewmodel.CountryListViewModel
67
import io.ktor.client.HttpClient
78
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
89
import io.ktor.client.plugins.logging.DEFAULT
@@ -27,7 +28,8 @@ fun commonModule(enableNetworkLogs: Boolean = false) = module {
2728
single { createJson() }
2829
single { createHttpClient(get(), enableNetworkLogs = enableNetworkLogs) }
2930
single { ClimateTraceApi(get()) }
30-
single { ClimateTraceViewModel() }
31+
single { CountryListViewModel() }
32+
single { CountryDetailsViewModel() }
3133
single { ClimateTraceRepository(get(), get()) }
3234
loadKoinModules(dataModule())
3335
}

composeApp/src/commonMain/kotlin/dev/johnoreilly/climatetrace/remote/ClimateTraceApi.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,5 +76,5 @@ class ClimateTraceApi(
7676
suspend fun fetchCountryAssets(countryCode: String) = client.get("$baseUrl/assets?countries=$countryCode").body<AssetsResult>()
7777
suspend fun fetchCountryEmissionsInfo(countryCode: String, year: String) = client.get("$baseUrl/country/emissions?since=$year&to=$year&countries=$countryCode").body<List<CountryEmissionsInfo>>()
7878

79-
suspend fun fetchCountryAssetEmissionsInfo(countryCode: String) = client.get("$baseUrl/assets/emissions?countries=$countryCode").body<Map<String, List<CountryAssetEmissionsInfo>>>()[countryCode]
79+
suspend fun fetchCountryAssetEmissionsInfo(countryCode: String) = client.get("$baseUrl/assets/emissions?countries=$countryCode").body<Map<String, List<CountryAssetEmissionsInfo>>>()[countryCode] ?: emptyList()
8080
}

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

Lines changed: 81 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -51,133 +51,129 @@ import cafe.adriel.voyager.core.screen.Screen
5151
import dev.johnoreilly.climatetrace.remote.Country
5252
import dev.johnoreilly.climatetrace.ui.utils.PanelState
5353
import dev.johnoreilly.climatetrace.ui.utils.ResizablePanel
54-
import dev.johnoreilly.climatetrace.viewmodel.ClimateTraceViewModel
54+
import dev.johnoreilly.climatetrace.viewmodel.CountryDetailsViewModel
55+
import dev.johnoreilly.climatetrace.viewmodel.CountryListUIState
56+
import dev.johnoreilly.climatetrace.viewmodel.CountryListViewModel
5557
import org.koin.compose.koinInject
5658

5759

58-
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
5960
class ClimateTraceScreen: Screen {
6061
@Composable
6162
override fun Content() {
62-
val windowSizeClass = calculateWindowSizeClass()
63-
val viewModel = koinInject<ClimateTraceViewModel>()
64-
65-
val countryList by viewModel.countryList.collectAsState()
66-
val selectedCountry by viewModel.selectedCountry.collectAsState()
67-
val countryEmissionInfo by viewModel.countryEmissionInfo.collectAsState()
68-
val countryAssetEmissions by viewModel.countryAssetEmissions.collectAsState()
69-
70-
val isLoadingCountries by viewModel.isLoadingCountries.collectAsState()
71-
val isLoadingCountryDetails by viewModel.isLoadingCountryDetails.collectAsState()
63+
val countryListViewModel = koinInject<CountryListViewModel>()
64+
val countryListViewState by countryListViewModel.viewState.collectAsState()
7265

66+
Column(Modifier) {
67+
when (val state = countryListViewState) {
68+
is CountryListUIState.Loading -> {
69+
Column(modifier = Modifier.fillMaxSize().fillMaxHeight()
70+
.wrapContentSize(Alignment.Center)
71+
) {
72+
CircularProgressIndicator()
73+
}
74+
}
75+
is CountryListUIState.Error -> {
76+
Text("Error")
77+
}
78+
is CountryListUIState.Success -> {
79+
CountryScreenSuccess(state.countryList)
80+
}
81+
}
82+
}
83+
}
84+
}
7385

74-
val panelState = remember { PanelState() }
86+
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
87+
@Composable
88+
fun CountryScreenSuccess(countryList: List<Country>) {
89+
val windowSizeClass = calculateWindowSizeClass()
90+
val countryDetailsViewModel = koinInject<CountryDetailsViewModel>()
7591

76-
val animatedSize = if (panelState.splitter.isResizing) {
77-
if (panelState.isExpanded) panelState.expandedSize else panelState.collapsedSize
78-
} else {
79-
animateDpAsState(
80-
if (panelState.isExpanded) panelState.expandedSize else panelState.collapsedSize,
81-
SpringSpec(stiffness = Spring.StiffnessLow)
82-
).value
83-
}
8492

93+
val selectedCountry by countryDetailsViewModel.selectedCountry.collectAsState()
94+
val countryDetailsViewState by countryDetailsViewModel.viewState.collectAsState()
8595

86-
Row(Modifier.fillMaxSize()) {
96+
val panelState = remember { PanelState() }
8797

88-
if (windowSizeClass.widthSizeClass == WindowWidthSizeClass.Compact) {
89-
Column(Modifier.fillMaxWidth()) {
98+
val animatedSize = if (panelState.splitter.isResizing) {
99+
if (panelState.isExpanded) panelState.expandedSize else panelState.collapsedSize
100+
} else {
101+
animateDpAsState(
102+
if (panelState.isExpanded) panelState.expandedSize else panelState.collapsedSize,
103+
SpringSpec(stiffness = Spring.StiffnessLow)
104+
).value
105+
}
90106

91-
Box(
92-
Modifier.height(250.dp).fillMaxWidth().background(color = Color.LightGray)
93-
) {
94-
CountryListView(
95-
countryList = countryList,
96-
selectedCountry = selectedCountry,
97-
isLoading = isLoadingCountries
98-
) { country ->
99-
viewModel.setCountry(country)
100-
}
101-
}
102107

103-
Spacer(modifier = Modifier.width(1.dp).fillMaxWidth())
104-
CountryInfoDetailedView(
105-
country = selectedCountry,
106-
year = viewModel.selectedYear.value,
107-
onYearSelected = { viewModel.setYear(it) },
108-
countryEmissionInfo = countryEmissionInfo,
109-
countryAssetEmissionsList = countryAssetEmissions,
110-
isLoading = isLoadingCountryDetails
111-
)
112-
}
113-
} else {
108+
Row(Modifier.fillMaxSize()) {
109+
if (windowSizeClass.widthSizeClass == WindowWidthSizeClass.Compact) {
110+
Column(Modifier.fillMaxWidth()) {
114111

115-
ResizablePanel(
116-
Modifier.width(animatedSize).fillMaxHeight(),
117-
title = "Countries",
118-
state = panelState
112+
Box(
113+
Modifier.height(250.dp).fillMaxWidth().background(color = Color.LightGray)
119114
) {
120115
CountryListView(
121116
countryList = countryList,
122117
selectedCountry = selectedCountry,
123-
isLoading = isLoadingCountries
124118
) { country ->
125-
viewModel.setCountry(country)
119+
countryDetailsViewModel.setCountry(country)
126120
}
127121
}
128122

129-
VerticalDivider(thickness = 1.dp, color = Color.DarkGray)
130-
Box(Modifier.fillMaxHeight()) {
131-
CountryInfoDetailedView(
132-
country = viewModel.selectedCountry.value,
133-
year = viewModel.selectedYear.value,
134-
onYearSelected = { viewModel.setYear(it) },
135-
countryEmissionInfo = countryEmissionInfo,
136-
countryAssetEmissionsList = countryAssetEmissions,
137-
isLoading = isLoadingCountryDetails
138-
)
123+
Spacer(modifier = Modifier.width(1.dp).fillMaxWidth())
124+
CountryInfoDetailedView(countryDetailsViewState) {
125+
countryDetailsViewModel.setYear(it)
126+
}
127+
}
128+
} else {
129+
130+
ResizablePanel(
131+
Modifier.width(animatedSize).fillMaxHeight(),
132+
title = "Countries",
133+
state = panelState
134+
) {
135+
CountryListView(
136+
countryList = countryList,
137+
selectedCountry = selectedCountry,
138+
) { country ->
139+
countryDetailsViewModel.setCountry(country)
140+
}
141+
}
142+
143+
VerticalDivider(thickness = 1.dp, color = Color.DarkGray)
144+
Box(Modifier.fillMaxHeight()) {
145+
CountryInfoDetailedView(countryDetailsViewState) {
146+
countryDetailsViewModel.setYear(it)
139147
}
140148
}
141149
}
142150
}
151+
143152
}
144153

154+
145155
@Composable
146156
fun CountryListView(
147157
countryList: List<Country>,
148158
selectedCountry: Country?,
149-
isLoading: Boolean,
150159
countrySelected: (country: Country) -> Unit
151160
) {
152161
val searchQuery = remember { mutableStateOf("") }
153162

154-
if (isLoading) {
155-
Column(
156-
modifier = Modifier
157-
.fillMaxSize()
158-
.fillMaxHeight()
159-
.wrapContentSize(Alignment.Center)
160-
) {
161-
CircularProgressIndicator()
162-
}
163-
} else {
164-
Column {
165-
SearchableList(
166-
isLoading = isLoading,
167-
searchQuery = searchQuery,
168-
onSearchQueryChange = { query -> searchQuery.value = query },
169-
countryList = countryList,
170-
selectedCountry = selectedCountry,
171-
countrySelected = countrySelected
172-
)
173-
}
163+
Column {
164+
SearchableList(
165+
searchQuery = searchQuery,
166+
onSearchQueryChange = { query -> searchQuery.value = query },
167+
countryList = countryList,
168+
selectedCountry = selectedCountry,
169+
countrySelected = countrySelected
170+
)
174171
}
175172
}
176173

177174
@OptIn(ExperimentalMaterial3Api::class)
178175
@Composable
179176
fun SearchableList(
180-
isLoading: Boolean,
181177
searchQuery: MutableState<String>,
182178
onSearchQueryChange: (String) -> Unit,
183179
countryList: List<Country>,
@@ -223,7 +219,7 @@ fun SearchableList(
223219
}
224220
},
225221
content = {
226-
if (filteredCountryList.isEmpty() && isLoading.not()) {
222+
if (filteredCountryList.isEmpty()) {
227223
EmptyState(message = "")
228224
} else {
229225
LazyColumn {

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

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import cafe.adriel.voyager.core.screen.Screen
1414
import cafe.adriel.voyager.navigator.LocalNavigator
1515
import cafe.adriel.voyager.navigator.currentOrThrow
1616
import dev.johnoreilly.climatetrace.remote.Country
17-
import dev.johnoreilly.climatetrace.viewmodel.ClimateTraceViewModel
17+
import dev.johnoreilly.climatetrace.viewmodel.CountryDetailsViewModel
1818
import org.koin.compose.koinInject
1919

2020
@OptIn(ExperimentalMaterial3Api::class)
@@ -24,10 +24,8 @@ data class CountryEmissionsScreen(val country: Country) : Screen {
2424
override fun Content() {
2525
val navigator = LocalNavigator.currentOrThrow
2626

27-
val viewModel = koinInject<ClimateTraceViewModel>()
28-
val countryEmissionInfo by viewModel.countryEmissionInfo.collectAsState()
29-
val countryAssetEmissions by viewModel.countryAssetEmissions.collectAsState()
30-
val isLoadingCountryDetails by viewModel.isLoadingCountryDetails.collectAsState()
27+
val viewModel = koinInject<CountryDetailsViewModel>()
28+
val viewState by viewModel.viewState.collectAsState()
3129

3230
LaunchedEffect(country) {
3331
viewModel.setCountry(country)
@@ -48,14 +46,9 @@ data class CountryEmissionsScreen(val country: Country) : Screen {
4846
}
4947
) {
5048
Column(Modifier.padding(it)) {
51-
CountryInfoDetailedView(
52-
country,
53-
viewModel.selectedYear.value,
54-
onYearSelected = { viewModel.setYear(it) },
55-
countryEmissionInfo,
56-
countryAssetEmissions,
57-
isLoadingCountryDetails
58-
)
49+
CountryInfoDetailedView(viewState) { year ->
50+
viewModel.setYear(year)
51+
}
5952
}
6053
}
6154
}

0 commit comments

Comments
 (0)