Finish up multiple locations support
This commit is contained in:
parent
8ff9b5ff4d
commit
edeea501eb
9 changed files with 82 additions and 72 deletions
|
@ -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) }
|
||||
|
|
|
@ -19,53 +19,68 @@ 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 = {
|
||||
ModalDrawerSheet {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
val locations = location.locations
|
||||
ModalNavigationDrawer(
|
||||
drawerContent = {
|
||||
ModalDrawerSheet {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
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 ->
|
||||
NavigationDrawerItem(
|
||||
label = { Text(text = data.location) },
|
||||
selected = index == location.selectedIndex,
|
||||
onClick = { location.selectedIndex = index },
|
||||
badge = {
|
||||
IconButton(onClick = { location.locations -= data }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Delete,
|
||||
contentDescription = stringResource(
|
||||
id = R.string.action_delete
|
||||
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 = selected,
|
||||
onClick = {
|
||||
onClose()
|
||||
locationPreferenceManager.selectedIndex = index
|
||||
},
|
||||
badge = {
|
||||
IconButton(onClick = {
|
||||
locationPreferenceManager.locations -= data
|
||||
if (selected) locationPreferenceManager.selectedIndex = 0
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Delete,
|
||||
contentDescription = stringResource(
|
||||
id = R.string.action_delete
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
NavigationDrawerItem(
|
||||
label = { Text(text = stringResource(id = R.string.location_add)) },
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Add,
|
||||
contentDescription = stringResource(id = R.string.location_add)
|
||||
)
|
||||
},
|
||||
selected = true,
|
||||
onClick = { navigator?.push(LocationPickerScreen()) },
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
NavigationDrawerItem(
|
||||
label = { Text(text = stringResource(id = R.string.location_add)) },
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Add,
|
||||
contentDescription = stringResource(id = R.string.location_add)
|
||||
)
|
||||
},
|
||||
selected = true,
|
||||
onClick = { navigator?.push(LocationPickerScreen()) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}, drawerState = drawerState) {
|
||||
},
|
||||
drawerState = drawerState
|
||||
) {
|
||||
children()
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
)
|
||||
|
|
|
@ -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 }
|
||||
}) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -43,7 +43,7 @@ object WeekTab : NavigationTab {
|
|||
override fun Content() {
|
||||
val weatherViewModel = getScreenModel<DailyWeatherScreenModel>()
|
||||
|
||||
LaunchedEffect(key1 = false) {
|
||||
LaunchedEffect(key1 = weatherViewModel.locationPreferenceManager.selectedIndex) {
|
||||
weatherViewModel.loadWeatherInfo()
|
||||
}
|
||||
|
||||
|
|
Reference in a new issue