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+ 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