1
+ import androidx.compose.foundation.layout.Column
2
+ import androidx.compose.foundation.layout.Spacer
3
+ import androidx.compose.foundation.layout.fillMaxHeight
4
+ import androidx.compose.foundation.layout.fillMaxSize
5
+ import androidx.compose.foundation.layout.padding
6
+ import androidx.compose.foundation.layout.size
7
+ import androidx.compose.foundation.layout.wrapContentSize
8
+ import androidx.compose.foundation.rememberScrollState
9
+ import androidx.compose.foundation.verticalScroll
10
+ import androidx.compose.material.icons.Icons
11
+ import androidx.compose.material.icons.automirrored.filled.ArrowBack
12
+ import androidx.compose.material3.CenterAlignedTopAppBar
13
+ import androidx.compose.material3.CircularProgressIndicator
14
+ import androidx.compose.material3.ExperimentalMaterial3Api
15
+ import androidx.compose.material3.Icon
16
+ import androidx.compose.material3.IconButton
1
17
import androidx.compose.material3.MaterialTheme
18
+ import androidx.compose.material3.Scaffold
19
+ import androidx.compose.material3.Text
2
20
import androidx.compose.runtime.Composable
21
+ import androidx.compose.runtime.LaunchedEffect
22
+ import androidx.compose.runtime.collectAsState
23
+ import androidx.compose.runtime.getValue
24
+ import androidx.compose.ui.Alignment
25
+ import androidx.compose.ui.Modifier
26
+ import androidx.compose.ui.graphics.Color
27
+ import androidx.compose.ui.text.style.TextAlign
28
+ import androidx.compose.ui.unit.dp
29
+ import androidx.navigation.compose.NavHost
30
+ import androidx.navigation.compose.composable
31
+ import androidx.navigation.compose.rememberNavController
32
+ import androidx.navigation.toRoute
3
33
import cafe.adriel.voyager.navigator.Navigator
4
34
import dev.johnoreilly.climatetrace.di.commonModule
35
+ import dev.johnoreilly.climatetrace.remote.Country
5
36
import dev.johnoreilly.climatetrace.ui.ClimateTraceScreen
37
+ import dev.johnoreilly.climatetrace.ui.CountryAssetEmissionsInfoTreeMapChart
38
+ import dev.johnoreilly.climatetrace.ui.CountryListView
39
+ import dev.johnoreilly.climatetrace.ui.SectorEmissionsPieChart
40
+ import dev.johnoreilly.climatetrace.ui.YearSelector
41
+ import dev.johnoreilly.climatetrace.ui.toPercent
42
+ import dev.johnoreilly.climatetrace.viewmodel.CountryDetailsUIState
43
+ import dev.johnoreilly.climatetrace.viewmodel.CountryDetailsViewModel
44
+ import dev.johnoreilly.climatetrace.viewmodel.CountryListUIState
45
+ import dev.johnoreilly.climatetrace.viewmodel.CountryListViewModel
46
+ import kotlinx.serialization.Serializable
6
47
import org.jetbrains.compose.ui.tooling.preview.Preview
7
48
import org.koin.compose.KoinApplication
49
+ import org.koin.compose.koinInject
8
50
9
51
10
52
@Preview
11
53
@Composable
12
- fun App () {
54
+ fun AppVoyagerNav () {
13
55
KoinApplication (application = {
14
56
modules(commonModule())
15
57
}) {
16
58
MaterialTheme {
17
59
Navigator (screen = ClimateTraceScreen ())
18
60
}
19
61
}
20
- }
62
+ }
63
+
64
+ @Serializable
65
+ object CountryList
66
+
67
+ @Composable
68
+ fun AppJetpackBav () {
69
+ KoinApplication (application = {
70
+ modules(commonModule())
71
+ }) {
72
+ MaterialTheme {
73
+ val navController = rememberNavController()
74
+
75
+ NavHost (
76
+ navController = navController,
77
+ startDestination = CountryList ,
78
+ ) {
79
+ composable<CountryList > {
80
+ CountryListScreenJetpackNav { country ->
81
+ navController.navigate(country)
82
+ }
83
+ }
84
+ composable<Country > { backStackEntry ->
85
+ val country: Country = backStackEntry.toRoute()
86
+ CountryInfoDetailedViewJetpackNav (country, popBack = { navController.popBackStack() })
87
+ }
88
+ }
89
+ }
90
+ }
91
+ }
92
+
93
+
94
+ @OptIn(ExperimentalMaterial3Api ::class )
95
+ @Composable
96
+ fun CountryListScreenJetpackNav (countrySelected : (country: Country ) -> Unit ) {
97
+ val viewModel = koinInject<CountryListViewModel >()
98
+ val viewState by viewModel.viewState.collectAsState()
99
+
100
+ Scaffold (
101
+ topBar = {
102
+ CenterAlignedTopAppBar (title = {
103
+ Text (" ClimateTraceKMP" )
104
+ }
105
+ )
106
+ }
107
+ ) {
108
+ Column (Modifier .padding(it)) {
109
+ when (val state = viewState) {
110
+ is CountryListUIState .Loading -> {
111
+ Column (
112
+ modifier = Modifier .fillMaxSize().fillMaxHeight()
113
+ .wrapContentSize(Alignment .Center )
114
+ ) {
115
+ CircularProgressIndicator ()
116
+ }
117
+ }
118
+
119
+ is CountryListUIState .Error -> {}
120
+ is CountryListUIState .Success -> {
121
+ CountryListView (state.countryList, null , countrySelected)
122
+ }
123
+ }
124
+ }
125
+ }
126
+ }
127
+
128
+
129
+ @Composable
130
+ fun CountryInfoDetailedViewJetpackNav (
131
+ country : Country ,
132
+ popBack : () -> Unit
133
+ ) {
134
+ val countryDetailsViewModel: CountryDetailsViewModel = koinInject()
135
+ val countryDetailsViewState by countryDetailsViewModel.viewState.collectAsState()
136
+
137
+ LaunchedEffect (country) {
138
+ countryDetailsViewModel.setCountry(country)
139
+ }
140
+
141
+ val viewState = countryDetailsViewState
142
+ when (viewState) {
143
+ CountryDetailsUIState .NoCountrySelected -> {
144
+ Column (
145
+ modifier = Modifier .fillMaxSize()
146
+ .wrapContentSize(Alignment .Center )
147
+ ) {
148
+ Text (text = " No Country Selected." , style = MaterialTheme .typography.titleMedium)
149
+ }
150
+ }
151
+ is CountryDetailsUIState .Loading -> {
152
+ Column (
153
+ modifier = Modifier .fillMaxSize()
154
+ .wrapContentSize(Alignment .Center )
155
+ ) {
156
+ CircularProgressIndicator ()
157
+ }
158
+ }
159
+ is CountryDetailsUIState .Error -> { Text (" Error" ) }
160
+ is CountryDetailsUIState .Success -> {
161
+ CountryInfoDetailedViewSuccessJetpackNav (viewState, popBack) {
162
+ countryDetailsViewModel.setYear(it)
163
+ }
164
+ }
165
+ }
166
+ }
167
+
168
+
169
+ @OptIn(ExperimentalMaterial3Api ::class )
170
+ @Composable
171
+ fun CountryInfoDetailedViewSuccessJetpackNav (viewState : CountryDetailsUIState .Success , popBack : () -> Unit , onYearSelected : (String ) -> Unit ) {
172
+
173
+ Scaffold (
174
+ topBar = {
175
+ CenterAlignedTopAppBar (
176
+ title = { Text (viewState.country.name) },
177
+ navigationIcon = {
178
+ IconButton (onClick = { popBack() }) {
179
+ Icon (Icons .AutoMirrored .Filled .ArrowBack , contentDescription = " Back" )
180
+ }
181
+ }
182
+ )
183
+ }
184
+ ) {
185
+
186
+ Column (
187
+ modifier = Modifier
188
+ .verticalScroll(rememberScrollState())
189
+ .fillMaxSize()
190
+ .padding(16 .dp),
191
+ horizontalAlignment = Alignment .CenterHorizontally
192
+ ) {
193
+
194
+ Text (
195
+ text = viewState.country.name,
196
+ style = MaterialTheme .typography.titleLarge,
197
+ textAlign = TextAlign .Center
198
+ )
199
+
200
+ Spacer (modifier = Modifier .size(16 .dp))
201
+
202
+ val year = viewState.year
203
+ val countryAssetEmissionsList = viewState.countryAssetEmissionsList
204
+ val countryEmissionInfo = viewState.countryEmissionInfo
205
+
206
+ YearSelector (year, onYearSelected)
207
+ countryEmissionInfo?.let {
208
+ val co2 = (countryEmissionInfo.emissions.co2 / 1_000_000 ).toInt()
209
+ val percentage =
210
+ (countryEmissionInfo.emissions.co2 / countryEmissionInfo.worldEmissions.co2).toPercent(
211
+ 2
212
+ )
213
+
214
+ Text (text = " co2 = $co2 Million Tonnes ($year )" )
215
+ Text (text = " rank = ${countryEmissionInfo.rank} ($percentage )" )
216
+
217
+ Spacer (modifier = Modifier .size(16 .dp))
218
+
219
+ val filteredCountryAssetEmissionsList =
220
+ countryAssetEmissionsList.filter { it.sector != null }
221
+ if (filteredCountryAssetEmissionsList.isNotEmpty()) {
222
+ SectorEmissionsPieChart (countryAssetEmissionsList)
223
+ Spacer (modifier = Modifier .size(32 .dp))
224
+ CountryAssetEmissionsInfoTreeMapChart (countryAssetEmissionsList)
225
+ } else {
226
+ Spacer (modifier = Modifier .size(16 .dp))
227
+ Column (horizontalAlignment = Alignment .CenterHorizontally ) {
228
+ Text (
229
+ " Invalid data" ,
230
+ style = MaterialTheme .typography.titleMedium.copy(color = Color .Red ),
231
+ textAlign = TextAlign .Center
232
+ )
233
+ }
234
+ }
235
+ }
236
+ }
237
+ }
238
+ }
0 commit comments