mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Cleaned up EntriesScreen and ViewModel logic
This commit is contained in:
1 parent
1aefb04eb5
commit
49f713d82c
5 files changed
+89
-56
No files matched your search
@@ -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() }
|
||||
|
||||
@@ -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<String>, 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) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Int>, 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<Int>, 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<Int>, 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<SatItem>, onSelected: (List<Int>, Boolean) -> Unit) {
|
||||
fun EntriesCard(items: List<SatItem>, onSelected: (Int, Boolean) -> Unit) {
|
||||
LazyColumn {
|
||||
items(items = items, key = { item -> item.catnum }) { entry ->
|
||||
Entry(entry, onSelected, Modifier.animateItemPlacement())
|
||||
|
||||
@@ -7,4 +7,14 @@ data class EntriesUiState(
|
||||
val itemsList: List<SatItem>,
|
||||
val currentType: String,
|
||||
val typesList: List<String>,
|
||||
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()
|
||||
}
|
||||
@@ -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<Int>, 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 {
|
||||
|
||||
Reference in new issue
Block a user