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
+ CountryInfoDetailedViewJetpackNav (country, popBack = { navController.popBackStack() })
85
+ }
86
+ }
87
+ }
88
+ }
89
+ }
90
+
91
+
92
+ @OptIn(ExperimentalMaterial3Api ::class )
93
+ @Composable
94
+ fun CountryListScreenJetpackNav (countrySelected : (country: Country ) -> Unit ) {
95
+ val viewModel = koinInject<CountryListViewModel >()
96
+ val viewState by viewModel.viewState.collectAsState()
97
+
98
+ Scaffold (
99
+ topBar = {
100
+ CenterAlignedTopAppBar (title = {
101
+ Text (" ClimateTraceKMP" )
102
+ }
103
+ )
104
+ }
105
+ ) {
106
+ Column (Modifier .padding(it)) {
107
+ when (val state = viewState) {
108
+ is CountryListUIState .Loading -> {
109
+ Column (
110
+ modifier = Modifier .fillMaxSize().fillMaxHeight()
111
+ .wrapContentSize(Alignment .Center )
112
+ ) {
113
+ CircularProgressIndicator ()
114
+ }
115
+ }
116
+
117
+ is CountryListUIState .Error -> {}
118
+ is CountryListUIState .Success -> {
119
+ CountryListView (state.countryList, null , countrySelected)
120
+ }
121
+ }
122
+ }
123
+ }
124
+ }
125
+
126
+
127
+ @Composable
128
+ fun CountryInfoDetailedViewJetpackNav (
129
+ country : Country ,
130
+ popBack : () -> Unit
131
+ ) {
132
+ val countryDetailsViewModel: CountryDetailsViewModel = koinInject()
133
+ val countryDetailsViewState by countryDetailsViewModel.viewState.collectAsState()
134
+
135
+ LaunchedEffect (country) {
136
+ countryDetailsViewModel.setCountry(country)
137
+ }
138
+
139
+ val viewState = countryDetailsViewState
140
+ when (viewState) {
141
+ CountryDetailsUIState .NoCountrySelected -> {
142
+ Column (
143
+ modifier = Modifier .fillMaxSize()
144
+ .wrapContentSize(Alignment .Center )
145
+ ) {
146
+ Text (text = " No Country Selected." , style = MaterialTheme .typography.titleMedium)
147
+ }
148
+ }
149
+ is CountryDetailsUIState .Loading -> {
150
+ Column (
151
+ modifier = Modifier .fillMaxSize()
152
+ .wrapContentSize(Alignment .Center )
153
+ ) {
154
+ CircularProgressIndicator ()
155
+ }
156
+ }
157
+ is CountryDetailsUIState .Error -> { Text (" Error" ) }
158
+ is CountryDetailsUIState .Success -> {
159
+ CountryInfoDetailedViewSuccessJetpackNav (viewState, popBack) {
160
+ countryDetailsViewModel.setYear(it)
161
+ }
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