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