build: S04 day detail

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-17 13:52:42 +02:00
co-authored by Claude Opus 5.5
parent c5c1483544
commit 1d8c3f8f5f
@@ -0,0 +1,112 @@
package app.wetter.ui.screens
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import app.wetter.R
import app.wetter.data.Fmt
import app.wetter.data.Graph
import app.wetter.data.Settings
import app.wetter.data.Units
import app.wetter.data.conditionOf
import app.wetter.ui.components.DailyCapsules
import app.wetter.ui.components.HourlyStrip
import app.wetter.ui.components.conditionLabel
import app.wetter.ui.scene.WeatherIcon
import app.wetter.ui.theme.WSpace
import app.wetter.ui.theme.WType
import java.time.LocalDate
/** S04: one day, picked from the capsule row. */
@Composable
fun DayScreen(locationId: Long, initialDate: LocalDate, settings: Settings, onBack: () -> Unit) {
val forecast by remember(locationId) { Graph.repo.forecast(locationId) }.collectAsState(initial = null)
val location by produceState<app.wetter.data.LocationEntity?>(null, locationId) { value = Graph.repo.location(locationId) }
val f = forecast ?: return
DayContent(f, location?.name?.ifBlank { null }, initialDate, settings, onBack)
}
@Composable
fun DayContent(f: app.wetter.data.Forecast, placeName: String?, initialDate: LocalDate, settings: Settings, onBack: () -> Unit) {
var date by rememberSaveable { mutableStateOf(initialDate.toEpochDay()) }
val units = remember(settings) { Units(settings) }
val day = f.daily.firstOrNull { it.date.toEpochDay() == date } ?: f.daily.first()
val cond = conditionOf(day.code)
val onBg = MaterialTheme.colorScheme.onBackground
Box(Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background)) {
LazyColumn(
Modifier.fillMaxSize(),
contentPadding = PaddingValues(bottom = WSpace.s32),
verticalArrangement = Arrangement.spacedBy(WSpace.s16),
) {
item {
Row(Modifier.statusBarsPadding().height(64.dp), verticalAlignment = Alignment.CenterVertically) {
IconButton(onClick = onBack) { Icon(Icons.AutoMirrored.Rounded.ArrowBack, stringResource(R.string.back), tint = onBg) }
Text(stringResource(R.string.daily), style = WType.headline, color = onBg)
}
}
item { DailyCapsules(f.daily, units, selected = day.date, asSection = false) { date = it.date.toEpochDay() } }
item {
Column(Modifier.padding(horizontal = WSpace.s24)) {
Text(Fmt.longDate(day.date), style = WType.bodySm, color = onBg)
Text(placeName ?: stringResource(R.string.current_location), style = WType.headline, color = onBg)
Row(verticalAlignment = Alignment.CenterVertically) {
Text("${units.temp(day.tMax)}/${units.temp(day.tMin)}", style = WType.dayRange, color = onBg)
Spacer(Modifier.width(WSpace.s8))
WeatherIcon(cond, true, 56.dp)
}
Text(stringResource(conditionLabel(cond, true)), style = WType.body, color = onBg)
}
}
item {
val hours = if (day.date == f.now.toLocalDate()) f.upcomingHours(24).filter { it.time.toLocalDate() == day.date } else f.hoursOf(day.date)
HourlyStrip(hours, units, Modifier.padding(horizontal = WSpace.s16).widthIn(max = 720.dp), markNow = day.date == f.now.toLocalDate())
}
item {
// the day's own values for the tiles that make sense per day
val dayHours = f.hoursOf(day.date)
val noon = dayHours.getOrNull(12)
val cur = if (day.date == f.now.toLocalDate() || noon == null) f.current else f.current.copy(
temp = noon.temp, code = noon.code, isDay = true, humidity = noon.humidity, windSpeed = noon.windSpeed, uv = day.uvMax,
)
app.wetter.ui.components.DetailGrid(
cur, day, if (day.date == f.now.toLocalDate()) f.air else null, units,
if (day.date == f.now.toLocalDate()) f.now else day.date.atTime(12, 0),
Modifier.padding(horizontal = WSpace.s16).navigationBarsPadding().widthIn(max = 720.dp).fillMaxWidth(),
)
}
}
}
}