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
117import androidx.compose.material3.MaterialTheme
18+ import androidx.compose.material3.Scaffold
19+ import androidx.compose.material3.Text
220import 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
333import cafe.adriel.voyager.navigator.Navigator
434import dev.johnoreilly.climatetrace.di.commonModule
35+ import dev.johnoreilly.climatetrace.remote.Country
536import 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
647import org.jetbrains.compose.ui.tooling.preview.Preview
748import org.koin.compose.KoinApplication
49+ import org.koin.compose.koinInject
850
951
1052@Preview
1153@Composable
12- fun App () {
54+ fun AppVoyagerNav () {
1355 KoinApplication (application = {
1456 modules(commonModule())
1557 }) {
1658 MaterialTheme {
1759 Navigator (screen = ClimateTraceScreen ())
1860 }
1961 }
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