Skip to content

Commit

Permalink
adds WeatherListScreen implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
MamboBryan committed Feb 5, 2024
1 parent 012eb73 commit 38e7e97
Show file tree
Hide file tree
Showing 11 changed files with 375 additions and 26 deletions.
1 change: 0 additions & 1 deletion composeApp/src/commonMain/kotlin/App.kt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import app.initKoin
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package data.repositories

import data.extensions.today
import data.helpers.DataResult
import data.mappers.fromDTO
import data.models.WeatherForecastData
import data.models.fromDTO
import kotlinx.datetime.DatePeriod
import kotlinx.datetime.LocalDate
import kotlinx.datetime.minus
import sources.remotesources.api.WeatherRemoteSource
import sources.remotesources.helpers.NetworkResult

Expand Down Expand Up @@ -40,8 +37,8 @@ class WeatherRepositoryImpl(private val remoteSource: WeatherRemoteSource) : Wea
): DataResult<List<WeatherForecastData>> {
val result = remoteSource.fetchPastWeatherDates(
city = "berlin",
startDate = today,
endDate = today.minus(period = DatePeriod(days = 14))
startDate = startDate,
endDate = endDate
)
return when (result) {
is NetworkResult.Error -> DataResult.Error(message = result.message)
Expand Down
2 changes: 2 additions & 0 deletions composeApp/src/commonMain/kotlin/domain/di/UseCaseModule.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package domain.di

import domain.usecases.GetCurrentWeatherDataUseCase
import domain.usecases.GetHistoryWeatherDataUseCase
import org.koin.dsl.module

/**
Expand All @@ -12,4 +13,5 @@ import org.koin.dsl.module

val useCaseModule = module {
factory { GetCurrentWeatherDataUseCase(weatherRepository = get()) }
factory { GetHistoryWeatherDataUseCase(weatherRepository = get()) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ class WeatherRemoteSourceImpl(private val client: HttpClient) : WeatherRemoteSou
)
}

/**
* Changes boolean value to valid request query parameter
*/
private fun Boolean.toQueryParameter() = when (this) {
true -> "yes"
false -> "no"
Expand All @@ -55,6 +58,7 @@ class WeatherRemoteSourceImpl(private val client: HttpClient) : WeatherRemoteSou
queries = listOf(
Query(name = "key", value = key),
Query(name = "q", value = city),
Query(name = "days", value = "$days"),
Query(name = "aqi", value = isAirQualityEnabled.toQueryParameter()),
Query(name = "alerts", value = isAlertsEnabled.toQueryParameter()),
)
Expand Down
193 changes: 193 additions & 0 deletions composeApp/src/commonMain/kotlin/ui/composables/WeatherListSection.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package ui.composables

import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Refresh
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.BaselineShift
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import data.models.WeatherForecastData
import io.kamel.image.KamelImage
import io.kamel.image.asyncPainterResource
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.painterResource
import ui.helpers.LoadState

/**
* PROJECT : weather
* DATE : Mon 05/02/2024
* TIME : 01:15
* USER : mambo
*/

@OptIn(ExperimentalResourceApi::class)
@Composable
fun WeatherListSection(
title: String,
state: LoadState<List<WeatherForecastData>>,
modifier: Modifier = Modifier,
isReversed: Boolean = false,
onClickRetry: () -> Unit = {},
onClickItem: (WeatherForecastData) -> Unit = {},
) {
Column(modifier = modifier) {
Text(
modifier = Modifier.background(Color.Gray.copy(alpha = 0.1f)).fillMaxWidth()
.padding(24.dp),
text = title
)
AnimatedContent(state) { value ->
when (value) {
LoadState.Loading -> {
CenteredColumn(modifier = Modifier.fillMaxWidth()) {
CircularProgressBar(modifier = Modifier.padding(48.dp))
}
}

is LoadState.Error -> {
CenteredColumn(modifier = Modifier.fillMaxWidth()) {
Column(
modifier = Modifier.padding(48.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
val painter = painterResource("images/error.jpg")
Image(
modifier = Modifier.padding(vertical = 8.dp).size(48.dp),
painter = painter,
contentDescription = null
)
Text(text = value.message)
TextButton(
modifier = Modifier.padding(top = 8.dp),
onClick = onClickRetry
) {
Text(text = "retry")
Icon(
modifier = Modifier.padding(start = 4.dp).size(16.dp),
imageVector = Icons.Rounded.Refresh,
contentDescription = null
)
}
}
}
}

is LoadState.Success -> {
LazyColumn(modifier = Modifier, reverseLayout = isReversed) {
items(value.data.size) { index ->
val data = value.data[index]
Column(
modifier = Modifier.clickable { onClickItem.invoke(data) }
.fillMaxWidth().padding(8.dp)
) {
Column(modifier = Modifier.fillMaxWidth()) {
Row(
modifier = Modifier.padding(16.dp).fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Column(modifier = Modifier.width(100.dp)) {
Text(
text = data.date.dayOfWeek.name.lowercase()
.replaceFirstChar { it.uppercase() })
Text(
text = "${data.date.dayOfMonth} ${
data.date.month.name.lowercase()
.replaceFirstChar { it.uppercase() }
.subSequence(0, 3)
}"
)
}

val painter =
asyncPainterResource(data = "https:" + data.day.condition.iconUrl)
KamelImage(
modifier = Modifier.size(48.dp),
resource = painter,
contentScale = ContentScale.Crop,
contentDescription = "Profile",
)

Text(
text = buildAnnotatedString {
withStyle(
style = SpanStyle(
fontWeight = FontWeight.Bold,
)
) {
append("${data.day.maxTemperatureInCelsius.toInt()}")
}
withStyle(
style = SpanStyle(
fontWeight = FontWeight.Bold,
fontSize = 12.sp,
baselineShift = BaselineShift.Superscript
)
) {
append("o")
}
append(" - ")
append("${data.day.minTemperatureInCelsius.toInt()}")
withStyle(
style = SpanStyle(
fontSize = 12.sp,
baselineShift = BaselineShift.Superscript
)
) {
append("o")
}
}
)

Text(
modifier = Modifier.width(100.dp),
text = data.day.condition.label,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.End,
maxLines = 1
)
}
if (index != value.data.lastIndex) {
Divider(
modifier = Modifier.fillMaxWidth(),
color = MaterialTheme.colors.onBackground.copy(0.1f)
)
}
}

}
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ui.screens.detail

import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
Expand Down Expand Up @@ -42,11 +43,14 @@ import cafe.adriel.voyager.koin.getScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator
import io.kamel.image.KamelImage
import io.kamel.image.asyncPainterResource
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.painterResource
import ui.composables.CenteredColumn
import ui.composables.CircularProgressBar
import ui.composables.WeatherHourItem
import ui.composables.WeatherInfoItem
import ui.helpers.LoadState
import ui.screens.list.WeatherListScreen

/**
* PROJECT : weather
Expand Down Expand Up @@ -76,6 +80,13 @@ object WeatherDetailScreen : Screen {
screenModel.getCurrentWeatherForecast()
}

LaunchedEffect(state.navigateToShowMore) {
if (state.navigateToShowMore) {
navigator?.push(WeatherListScreen(forecasts = state.forecasts))
screenModel.resetNavigation()
}
}

WeatherDetailScreenContent(
state = state,
onClickRetry = screenModel::getCurrentWeatherForecast,
Expand All @@ -84,6 +95,7 @@ object WeatherDetailScreen : Screen {
}
}

@OptIn(ExperimentalResourceApi::class)
@Composable
fun WeatherDetailScreenContent(
state: WeatherDetailScreenState,
Expand All @@ -92,16 +104,6 @@ fun WeatherDetailScreenContent(
) {
Scaffold { padding ->
Column(modifier = Modifier.padding(padding).fillMaxSize()) {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Nairobi",
fontWeight = FontWeight.Bold,
fontSize = 36.sp
)
}
Column(modifier = Modifier.fillMaxWidth().weight(1f)) {
AnimatedContent(targetState = state.weatherData) { value ->
when (value) {
Expand All @@ -110,10 +112,17 @@ fun WeatherDetailScreenContent(
modifier = Modifier.fillMaxSize()
.testTag(WeatherDetailScreen.Tags.Loading)
) {
CircularProgressBar(
modifier = Modifier.size(50.dp),
strokeWidth = 7.dp
)
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Image(
modifier = Modifier.size(150.dp).padding(bottom = 16.dp),
painter = painterResource("images/icon.png"),
contentDescription = "icon"
)
CircularProgressBar(
modifier = Modifier.size(50.dp),
strokeWidth = 7.dp
)
}
}
}

Expand Down Expand Up @@ -146,6 +155,32 @@ fun WeatherDetailScreenContent(
.fillMaxSize()
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Column(
modifier = Modifier,
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Berlin",
fontWeight = FontWeight.Bold,
fontSize = 36.sp
)
Text(
text = buildString {
append(
result.date.dayOfWeek.name.lowercase()
.replaceFirstChar { it.uppercase() }
)
append(",")
append(result.date.dayOfMonth)
append(" ")
append(
result.date.month.name.lowercase()
.replaceFirstChar { it.uppercase() }
)
}
)
}
Card(
modifier = Modifier
.size(200.dp)
Expand All @@ -165,7 +200,7 @@ fun WeatherDetailScreenContent(
fontWeight = FontWeight.Bold,
fontSize = 80.sp,
text = buildAnnotatedString {
append("${result.day.averageTemperatureInCelsius}")
append("${result.day.averageTemperatureInCelsius.toInt()}")
withStyle(
style = SpanStyle(
fontSize = 48.sp,
Expand Down
Loading

0 comments on commit 38e7e97

Please sign in to comment.