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
646import org.jetbrains.compose.ui.tooling.preview.Preview
747import org.koin.compose.KoinApplication
48+ import org.koin.compose.koinInject
849
950
1051@Preview
1152@Composable
12- fun App () {
53+ fun AppVoyagerNav () {
1354 KoinApplication (application = {
1455 modules(commonModule())
1556 }) {
1657 MaterialTheme {
1758 Navigator (screen = ClimateTraceScreen ())
1859 }
1960 }
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