feat: add loading state management to ListDetailViewModel and display loading overlay in ListDetailScreen

This commit is contained in:
2025-11-11 23:25:20 +01:00
parent a7fff59f8c
commit 74d2bf60cf
2 changed files with 54 additions and 17 deletions
@@ -121,6 +121,7 @@ fun ListDetailScreen(
val list by viewModel.list.collectAsState()
val fields by viewModel.fields.collectAsState()
val items by viewModel.items.collectAsState()
val isLoading by viewModel.isLoading.collectAsState()
// Use derivedStateOf to create stable references
val stableFields by remember { derivedStateOf { fields } }
@@ -239,15 +240,18 @@ fun ListDetailScreen(
val processedItems = transformed.processed
val groupedItems = transformed.grouped
if (stableFields.isEmpty()) {
Box(
modifier =
Modifier
.fillMaxSize()
.padding(padding),
contentAlignment = Alignment.Center,
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
// Root container to allow loading overlay
Box(
modifier =
Modifier
.fillMaxSize()
.padding(padding),
) {
if (stableFields.isEmpty()) {
Column(
modifier = Modifier.align(Alignment.Center),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = "No fields yet. Add fields to get started!",
style = MaterialTheme.typography.bodyLarge,
@@ -260,14 +264,12 @@ fun ListDetailScreen(
Text(stringResource(R.string.add_field))
}
}
}
} else {
Column(
modifier =
Modifier
.fillMaxSize()
.padding(padding),
) {
} else {
Column(
modifier =
Modifier
.fillMaxSize(),
) {
// Filter/Sort/Group Controls - Always visible above table
Row(
modifier =
@@ -560,6 +562,17 @@ fun ListDetailScreen(
)
}
}
// Loading overlay during initial content load
if (isLoading) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center,
) {
androidx.compose.material3.CircularProgressIndicator()
}
}
}
}
// Items list below the fixed header
@@ -34,6 +34,9 @@ class ListDetailViewModel(
private val _items = MutableStateFlow<List<ItemWithValues>>(emptyList())
val items: StateFlow<List<ItemWithValues>> = _items.asStateFlow()
private val _isLoading = MutableStateFlow(true)
val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()
private val syncRepository = SyncRepository(context)
init {
@@ -42,6 +45,15 @@ class ListDetailViewModel(
}
private fun loadListData() {
// Track first emissions to flip loading off after initial data is ready
var hasList = false
var hasFields = false
var hasItems = false
fun maybeLoaded() {
if (hasList && hasFields && hasItems) {
_isLoading.value = false
}
}
viewModelScope.launch {
database
.listDao()
@@ -49,6 +61,10 @@ class ListDetailViewModel(
.debounce(75)
.collect { listWithFields ->
_list.value = listWithFields?.list
if (!hasList) {
hasList = true
maybeLoaded()
}
}
}
@@ -59,6 +75,10 @@ class ListDetailViewModel(
.debounce(75)
.collect { itemsData ->
_items.value = itemsData
if (!hasItems) {
hasItems = true
maybeLoaded()
}
}
}
@@ -69,6 +89,10 @@ class ListDetailViewModel(
.debounce(75)
.collect { fieldsData ->
_fields.value = fieldsData
if (!hasFields) {
hasFields = true
maybeLoaded()
}
}
}
}