Finish up multiple locations support

This commit is contained in:
Henry Hiles 2023-05-06 11:02:38 -04:00
parent 8ff9b5ff4d
commit edeea501eb
9 changed files with 82 additions and 72 deletions

View file

@ -29,7 +29,7 @@ abstract class BasePreferenceManager(
enumValueOf<E>(getString(key, defaultValue.name))
protected inline fun <reified T> getJson(key: String, defaultValue: T) =
Json.decodeFromString<T>(getString(key, null)) ?: defaultValue
Json.decodeFromString<T>(getString(key, Json.encodeToString(defaultValue)))
protected fun putString(key: String, value: String?) = prefs.edit { putString(key, value) }
private fun putBoolean(key: String, value: Boolean) = prefs.edit { putBoolean(key, value) }

View file

@ -19,27 +19,40 @@ import com.henryhiles.qweather.presentation.screenmodel.LocationPreferenceManage
import org.koin.androidx.compose.get
@Composable
fun LocationsDrawer(drawerState: DrawerState, children: @Composable () -> Unit) {
val location: LocationPreferenceManager = get()
fun LocationsDrawer(
drawerState: DrawerState,
onClose: () -> Unit,
children: @Composable () -> Unit
) {
val locationPreferenceManager: LocationPreferenceManager = get()
val navigator = LocalNavigator.current?.parent
ModalNavigationDrawer(drawerContent = {
ModalNavigationDrawer(
drawerContent = {
ModalDrawerSheet {
Column(modifier = Modifier.padding(16.dp)) {
val locations = location.locations
val locations = locationPreferenceManager.locations
Text(
text = stringResource(id = R.string.locations),
style = MaterialTheme.typography.headlineSmall
)
Spacer(modifier = Modifier.height(16.dp))
locations.forEachIndexed { index, data ->
val selected = index == locationPreferenceManager.selectedIndex
NavigationDrawerItem(
label = { Text(text = data.location) },
selected = index == location.selectedIndex,
onClick = { location.selectedIndex = index },
selected = selected,
onClick = {
onClose()
locationPreferenceManager.selectedIndex = index
},
badge = {
IconButton(onClick = { location.locations -= data }) {
IconButton(onClick = {
locationPreferenceManager.locations -= data
if (selected) locationPreferenceManager.selectedIndex = 0
}) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = stringResource(
@ -65,7 +78,9 @@ fun LocationsDrawer(drawerState: DrawerState, children: @Composable () -> Unit)
)
}
}
}, drawerState = drawerState) {
},
drawerState = drawerState
) {
children()
}
}

View file

@ -1,8 +1,6 @@
package com.henryhiles.qweather.presentation.components.weather
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
@ -21,9 +19,8 @@ import androidx.compose.ui.unit.sp
import com.henryhiles.qweather.domain.weather.HourlyWeatherData
import java.time.format.DateTimeFormatter
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun WeatherCard(hour: HourlyWeatherData?, location: String, modifier: Modifier = Modifier) {
fun WeatherCard(hour: HourlyWeatherData?, modifier: Modifier = Modifier) {
hour?.let {
val formattedTime = remember(it) {
it.time.format(DateTimeFormatter.ofPattern("HH:mm"))
@ -42,13 +39,6 @@ fun WeatherCard(hour: HourlyWeatherData?, location: String, modifier: Modifier =
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth()
) {
Text(
text = location,
modifier = Modifier
.width(152.dp)
.basicMarquee(delayMillis = 2000, initialDelayMillis = 1000),
maxLines = 1,
)
Text(
text = "Today $formattedTime",
)

View file

@ -44,6 +44,7 @@ class LocationPickerScreen : Screen {
FloatingActionButton(onClick = {
location?.let {
screenModel.prefs.locations += it
screenModel.prefs.selectedIndex = screenModel.prefs.locations.count() - 1
navigator?.push(MainScreen())
} ?: kotlin.run { isAboutOpen = true }
}) {

View file

@ -1,21 +1,18 @@
package com.henryhiles.qweather.presentation.screen
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.CurrentScreen
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.tab.TabNavigator
import com.henryhiles.qweather.R
import com.henryhiles.qweather.domain.util.NavigationTab
@ -28,26 +25,34 @@ import kotlinx.coroutines.launch
import org.koin.androidx.compose.get
class MainScreen : Screen {
@OptIn(ExperimentalFoundationApi::class)
@Composable
override fun Content() {
val locationPreferenceManager: LocationPreferenceManager = get()
val drawerState =
rememberDrawerState(initialValue = DrawerValue.Closed)
val coroutineScope = rememberCoroutineScope()
TabNavigator(tab = TodayTab) {
LocationsDrawer(drawerState = drawerState) {
LocationsDrawer(
drawerState = drawerState,
onClose = { coroutineScope.launch { drawerState.close() } }) {
Scaffold(
topBar = {
SmallToolbar(
title = { Text(text = stringResource(R.string.app_name)) },
title = {
with(locationPreferenceManager) {
Text(
text = locations[selectedIndex].location,
maxLines = 1,
modifier = Modifier.basicMarquee()
)
}
},
actions = {
(it.current as? NavigationTab)?.Actions()
}
) {
IconButton(onClick = {
coroutineScope.launch {
with(drawerState) { if (isOpen) close() else open() }
}
}) {
IconButton(onClick = { coroutineScope.launch { drawerState.open() } }) {
Icon(
imageVector = Icons.Default.Menu,
contentDescription = stringResource(id = R.string.location_picker_open)

View file

@ -19,13 +19,13 @@ data class DailyWeatherState(
class DailyWeatherScreenModel(
private val repository: WeatherRepository,
locationPreferenceManager: LocationPreferenceManager
val locationPreferenceManager: LocationPreferenceManager
) : ScreenModel {
var state by mutableStateOf(DailyWeatherState())
private set
val location = locationPreferenceManager.locations[locationPreferenceManager.selectedIndex]
fun loadWeatherInfo(cache: Boolean = true) {
val location = locationPreferenceManager.locations[locationPreferenceManager.selectedIndex]
coroutineScope.launch {
state = state.copy(isLoading = true, error = null)
state = when (val result = repository.getDailyWeatherData(

View file

@ -19,14 +19,13 @@ data class HourlyWeatherState(
class HourlyWeatherScreenModel(
private val repository: WeatherRepository,
locationPreferenceManager: LocationPreferenceManager,
val locationPreferenceManager: LocationPreferenceManager,
) : ScreenModel {
var state by mutableStateOf(HourlyWeatherState())
private set
val location = locationPreferenceManager.locations[locationPreferenceManager.selectedIndex]
fun loadWeatherInfo(cache: Boolean = true) {
val location = locationPreferenceManager.locations[locationPreferenceManager.selectedIndex]
coroutineScope.launch {
state = state.copy(isLoading = true, error = null, selected = null)
state = when (val result =

View file

@ -43,7 +43,7 @@ object TodayTab : NavigationTab {
override fun Content() {
val weatherViewModel = getScreenModel<HourlyWeatherScreenModel>()
LaunchedEffect(key1 = false) {
LaunchedEffect(key1 = weatherViewModel.locationPreferenceManager.selectedIndex) {
weatherViewModel.loadWeatherInfo()
}
@ -78,13 +78,13 @@ object TodayTab : NavigationTab {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
WeatherCard(
hour = weatherViewModel.state.selected?.let {
weatherViewModel.state.hourlyWeatherInfo?.weatherData?.get(it)
} ?: weatherViewModel.state.hourlyWeatherInfo?.currentWeatherData,
location = weatherViewModel.location.location
)
WeatherToday(state = weatherViewModel.state)
WeatherForecast(

View file

@ -43,7 +43,7 @@ object WeekTab : NavigationTab {
override fun Content() {
val weatherViewModel = getScreenModel<DailyWeatherScreenModel>()
LaunchedEffect(key1 = false) {
LaunchedEffect(key1 = weatherViewModel.locationPreferenceManager.selectedIndex) {
weatherViewModel.loadWeatherInfo()
}