From 49f713d82ce68e864e1b8af074cce32de376a3b1 Mon Sep 17 00:00:00 2001 From: Arty Bishop Date: Sun, 13 Aug 2023 10:55:18 +0100 Subject: [PATCH] Cleaned up EntriesScreen and ViewModel logic --- .../look4sat/presentation/MainScreen.kt | 22 +++++--- .../presentation/dialogs/TypesDialog.kt | 30 ++++++----- .../presentation/entries/EntriesScreen.kt | 53 ++++++++++--------- .../presentation/entries/EntriesUiState.kt | 10 ++++ .../presentation/entries/EntriesViewModel.kt | 30 +++++++---- 5 files changed, 89 insertions(+), 56 deletions(-) diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/MainScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/MainScreen.kt index 77be1172..547bda74 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/MainScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/MainScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost @@ -20,6 +21,7 @@ import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument import com.rtbishop.look4sat.R import com.rtbishop.look4sat.presentation.entries.EntriesScreen +import com.rtbishop.look4sat.presentation.entries.EntriesViewModel import com.rtbishop.look4sat.presentation.map.MapScreen import com.rtbishop.look4sat.presentation.passes.PassesScreen import com.rtbishop.look4sat.presentation.radar.RadarScreen @@ -62,16 +64,20 @@ private fun MainNavBar(navController: NavController) { @Composable private fun MainNavGraph(navController: NavHostController) { - val navToPasses = { navController.navigate(Screen.Passes.route) } - val navToRadar = { catNum: Int, aosTime: Long -> - navController.navigate("${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}") - } val radarRoute = "${Screen.Radar.route}?catNum={catNum}&aosTime={aosTime}" - val radarArgs = listOf(navArgument("catNum") { defaultValue = 0 }, - navArgument("aosTime") { defaultValue = 0L }) + val radarArgs = listOf(navArgument("catNum") { defaultValue = 0 }, navArgument("aosTime") { defaultValue = 0L }) NavHost(navController, startDestination = Screen.Passes.route) { - composable(Screen.Entries.route) { EntriesScreen(navToPasses) } - composable(Screen.Passes.route) { PassesScreen(navToRadar) } + composable(Screen.Entries.route) { + val viewModel = viewModel(EntriesViewModel::class.java, factory = EntriesViewModel.Factory) + val navToPasses = { navController.navigate(Screen.Passes.route) } + EntriesScreen(viewModel.uiState.value, navToPasses) + } + composable(Screen.Passes.route) { + val navToRadar = { catNum: Int, aosTime: Long -> + navController.navigate("${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}") + } + PassesScreen(navToRadar) + } composable(radarRoute, radarArgs) { RadarScreen() } composable(Screen.Map.route) { MapScreen() } composable(Screen.Settings.route) { SettingsScreen() } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/dialogs/TypesDialog.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/dialogs/TypesDialog.kt index 3d9a1d00..55ea0c05 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/dialogs/TypesDialog.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/dialogs/TypesDialog.kt @@ -5,10 +5,9 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material3.ElevatedCard import androidx.compose.material3.MaterialTheme import androidx.compose.material3.RadioButton @@ -33,31 +32,36 @@ private fun TypeDialogPreview() { @Composable fun TypesDialog(list: List, selected: String, toggle: () -> Unit, click: (String) -> Unit) { + val clickAction = { type: String -> + click(type) + toggle() + } Dialog(onDismissRequest = { toggle() }) { - ElevatedCard(modifier = Modifier.fillMaxHeight(0.9f)) { + ElevatedCard(modifier = Modifier.fillMaxHeight(0.75f)) { LazyColumn( modifier = Modifier.background(MaterialTheme.colorScheme.background), verticalArrangement = Arrangement.spacedBy(1.dp) ) { - items(list) { type -> + itemsIndexed(list) { index, type -> Surface { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier .background(MaterialTheme.colorScheme.surface) - .clickable { - click(type) - toggle() - }) { - RadioButton(selected = type == selected, onClick = {}) + .clickable { clickAction(type) }) { + Text( + text = "$index).", + modifier = Modifier.padding(start = 12.dp, end = 6.dp), + fontWeight = FontWeight.Normal, + color = MaterialTheme.colorScheme.secondary + ) Text( text = type, - modifier = Modifier - .padding(end = 6.dp) - .fillMaxWidth(), - fontWeight = FontWeight.Normal, + modifier = Modifier.weight(1f), + fontWeight = FontWeight.Medium, maxLines = 1, overflow = TextOverflow.Ellipsis ) + RadioButton(selected = type == selected, onClick = { clickAction(type) }) } } } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesScreen.kt index 0886cf9d..8fc7a8a7 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesScreen.kt @@ -38,42 +38,38 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.lifecycle.viewmodel.compose.viewModel import com.rtbishop.look4sat.R import com.rtbishop.look4sat.domain.model.SatItem import com.rtbishop.look4sat.presentation.CardIcon import com.rtbishop.look4sat.presentation.CardLoadingIndicator import com.rtbishop.look4sat.presentation.MainTheme import com.rtbishop.look4sat.presentation.dialogs.TypesDialog -import com.rtbishop.look4sat.presentation.onClick @Composable -fun EntriesScreen(navToPasses: () -> Unit) { - val viewModel = viewModel(EntriesViewModel::class.java, factory = EntriesViewModel.Factory) - val uiState = viewModel.uiState.value - +fun EntriesScreen(uiState: EntriesUiState, navToPasses: () -> Unit) { val showDialog = rememberSaveable { mutableStateOf(false) } val toggleDialog = { showDialog.value = showDialog.value.not() } if (showDialog.value) { TypesDialog(list = uiState.typesList, selected = uiState.currentType, toggleDialog) { - viewModel.setType(it) + uiState.takeAction(EntriesUiAction.SelectType(it)) } } - val unselectAll = { viewModel.setSelection(false) } - val selectAll = { viewModel.setSelection(true) } + val unselectAll = { uiState.takeAction(EntriesUiAction.UnselectAll) } + val selectAll = { uiState.takeAction(EntriesUiAction.SelectAll) } Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) { - TopBar(setQuery = { newQuery: String -> viewModel.setQuery(newQuery) }, saveSelection = { - viewModel.saveSelection() - navToPasses() - }) + TopBar(setQuery = { newQuery: String -> uiState.takeAction(EntriesUiAction.SearchFor(newQuery)) }, + saveSelection = { + uiState.takeAction(EntriesUiAction.SaveSelection) + navToPasses() + }) MiddleBar(uiState.currentType, { toggleDialog() }, { unselectAll() }, { selectAll() }) ElevatedCard(modifier = Modifier.fillMaxSize()) { if (uiState.isLoading) { CardLoadingIndicator() } else { - EntriesCard(uiState.itemsList) { list, value -> - viewModel.setSelection(list, value) + EntriesCard(uiState.itemsList) { id, isTicked -> + uiState.takeAction(EntriesUiAction.SelectSingle(id, isTicked)) } } } @@ -188,19 +184,21 @@ private fun EntryTypeCard(type: String, onClick: () -> Unit, modifier: Modifier @Preview(showBackground = true) @Composable private fun EntryPreview() { - val satItem = SatItem(45555, "Very long Satellite name", true) + val satItem = SatItem(44444, "Ultra Super Mega long satellite name", true) MainTheme { Entry(item = satItem, onSelected = { _, _ -> run {} }, modifier = Modifier) } } @Composable -private fun Entry(item: SatItem, onSelected: (List, Boolean) -> Unit, modifier: Modifier) { - Surface(color = MaterialTheme.colorScheme.background, modifier = modifier) { +private fun Entry(item: SatItem, onSelected: (Int, Boolean) -> Unit, modifier: Modifier) { + Surface(color = MaterialTheme.colorScheme.background, + modifier = modifier.clickable { onSelected(item.catnum, item.isSelected) }) { Surface(modifier = Modifier.padding(bottom = 1.dp)) { - Row(verticalAlignment = Alignment.CenterVertically, + Row( + verticalAlignment = Alignment.CenterVertically, modifier = Modifier .background(MaterialTheme.colorScheme.surface) .padding(start = 0.dp, top = 8.dp, end = 12.dp, bottom = 8.dp) - .onClick { onSelected(listOf(item.catnum), item.isSelected.not()) }) { + ) { Text( text = "Id:${item.catnum} - ", modifier = Modifier.width(104.dp), @@ -209,14 +207,12 @@ private fun Entry(item: SatItem, onSelected: (List, Boolean) -> Unit, modif ) Text( text = item.name, - modifier = Modifier - .weight(1f) - .padding(end = 6.dp), + modifier = Modifier.weight(1f), fontWeight = FontWeight.Medium, maxLines = 1, overflow = TextOverflow.Ellipsis ) - Checkbox(checked = item.isSelected, onCheckedChange = null) + Checkbox(checked = item.isSelected, onCheckedChange = null, modifier = Modifier.padding(start = 6.dp)) } } } @@ -225,12 +221,17 @@ private fun Entry(item: SatItem, onSelected: (List, Boolean) -> Unit, modif @Preview(showBackground = true) @Composable private fun EntriesPreview() { - MainTheme { EntriesCard(emptyList()) { _, _ -> run {} } } + val entries = listOf( + SatItem(8888, "Meteor", false), + SatItem(44444, "ISS", true), + SatItem(88888, "Starlink", false) + ) + MainTheme { EntriesCard(entries) { _, _ -> run {} } } } @OptIn(ExperimentalFoundationApi::class) @Composable -fun EntriesCard(items: List, onSelected: (List, Boolean) -> Unit) { +fun EntriesCard(items: List, onSelected: (Int, Boolean) -> Unit) { LazyColumn { items(items = items, key = { item -> item.catnum }) { entry -> Entry(entry, onSelected, Modifier.animateItemPlacement()) diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesUiState.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesUiState.kt index c65200bb..2a59b80d 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesUiState.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesUiState.kt @@ -7,4 +7,14 @@ data class EntriesUiState( val itemsList: List, val currentType: String, val typesList: List, + val takeAction: (EntriesUiAction) -> Unit ) + +sealed class EntriesUiAction { + data object SaveSelection : EntriesUiAction() + data class SearchFor(val query: String) : EntriesUiAction() + data object SelectAll : EntriesUiAction() + data class SelectSingle(val id: Int, val isTicked: Boolean) : EntriesUiAction() + data class SelectType(val type: String) : EntriesUiAction() + data object UnselectAll : EntriesUiAction() +} diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesViewModel.kt index 24265703..d0181252 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesViewModel.kt @@ -34,7 +34,8 @@ class EntriesViewModel(private val selectionRepo: ISelectionRepo) : ViewModel() isLoading = true, itemsList = emptyList(), currentType = selectionRepo.getCurrentType(), - typesList = selectionRepo.getTypesList() + typesList = selectionRepo.getTypesList(), + takeAction = ::handleAction ) val uiState = mutableStateOf(defaultUiState) @@ -47,22 +48,33 @@ class EntriesViewModel(private val selectionRepo: ISelectionRepo) : ViewModel() } } - fun setType(type: String) = viewModelScope.launch { - selectionRepo.setType(type) - uiState.value = uiState.value.copy(currentType = type) + private fun handleAction(action: EntriesUiAction) { + when (action) { + EntriesUiAction.SaveSelection -> saveSelection() + is EntriesUiAction.SearchFor -> searchFor(action.query) + EntriesUiAction.SelectAll -> selectAll(true) + is EntriesUiAction.SelectSingle -> selectSingle(action.id, action.isTicked) + is EntriesUiAction.SelectType -> selectType(action.type) + EntriesUiAction.UnselectAll -> selectAll(false) + } } - fun setQuery(query: String) = viewModelScope.launch { selectionRepo.setQuery(query) } + private fun saveSelection() = viewModelScope.launch { selectionRepo.saveSelection() } - fun setSelection(selectAll: Boolean) = viewModelScope.launch { + private fun searchFor(query: String) = viewModelScope.launch { selectionRepo.setQuery(query) } + + private fun selectAll(selectAll: Boolean) = viewModelScope.launch { selectionRepo.setSelection(selectAll) } - fun setSelection(ids: List, isTicked: Boolean) = viewModelScope.launch { - selectionRepo.setSelection(ids, isTicked) + private fun selectSingle(id: Int, isTicked: Boolean) = viewModelScope.launch { + selectionRepo.setSelection(listOf(id), isTicked.not()) } - fun saveSelection() = viewModelScope.launch { selectionRepo.saveSelection() } + private fun selectType(type: String) = viewModelScope.launch { + selectionRepo.setType(type) + uiState.value = uiState.value.copy(currentType = type) + } companion object { val Factory: ViewModelProvider.Factory = viewModelFactory {