Implement error handling with ApiResultHandler on key screens
Applied the new error handling utilities to ResidencesScreen and ResidenceDetailScreen, replacing inline error displays with popup dialogs. Changes: - ResidencesScreen: Replaced inline error UI with ApiResultHandler - Shows error dialog popup instead of error message in center of screen - Maintains all functionality while improving UX - ResidenceDetailScreen: Comprehensive error handling implementation - Main residence loading uses ApiResultHandler with custom loading content - Generate report operation uses HandleErrors() extension - Delete residence operation uses HandleErrors() extension - All operations now show retry/cancel dialogs on error Benefits: - Consistent error handling UX across screens - Users can retry failed operations without navigating away - Cleaner code with less boilerplate error handling - Error dialogs are non-blocking and dismissible These screens now demonstrate the pattern for implementing error handling across the rest of the app using the error handling utilities. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,9 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.mycrib.android.ui.components.AddNewTaskDialog
|
||||
import com.mycrib.android.ui.components.ApiResultHandler
|
||||
import com.mycrib.android.ui.components.CompleteTaskDialog
|
||||
import com.mycrib.android.ui.components.HandleErrors
|
||||
import com.mycrib.android.ui.components.ManageUsersDialog
|
||||
import com.mycrib.android.ui.components.common.InfoCard
|
||||
import com.mycrib.android.ui.components.residence.PropertyDetailItem
|
||||
@@ -116,6 +118,12 @@ fun ResidenceDetailScreen(
|
||||
}
|
||||
|
||||
// Handle generate report state
|
||||
// Handle errors for generate report
|
||||
generateReportState.HandleErrors(
|
||||
onRetry = { residenceViewModel.generateMaintenanceReport(residenceId) },
|
||||
errorTitle = "Failed to Generate Report"
|
||||
)
|
||||
|
||||
LaunchedEffect(generateReportState) {
|
||||
when (generateReportState) {
|
||||
is ApiResult.Success -> {
|
||||
@@ -124,15 +132,16 @@ fun ResidenceDetailScreen(
|
||||
showReportSnackbar = true
|
||||
residenceViewModel.resetGenerateReportState()
|
||||
}
|
||||
is ApiResult.Error -> {
|
||||
reportMessage = (generateReportState as ApiResult.Error).message
|
||||
showReportSnackbar = true
|
||||
residenceViewModel.resetGenerateReportState()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle errors for delete residence
|
||||
deleteState.HandleErrors(
|
||||
onRetry = { residenceViewModel.deleteResidence(residenceId) },
|
||||
errorTitle = "Failed to Delete Property"
|
||||
)
|
||||
|
||||
// Handle delete residence state
|
||||
LaunchedEffect(deleteState) {
|
||||
when (deleteState) {
|
||||
@@ -140,11 +149,6 @@ fun ResidenceDetailScreen(
|
||||
residenceViewModel.resetDeleteResidenceState()
|
||||
onNavigateBack()
|
||||
}
|
||||
is ApiResult.Error -> {
|
||||
reportMessage = (deleteState as ApiResult.Error).message
|
||||
showReportSnackbar = true
|
||||
residenceViewModel.resetDeleteResidenceState()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
@@ -403,63 +407,29 @@ fun ResidenceDetailScreen(
|
||||
}
|
||||
}
|
||||
) { paddingValues ->
|
||||
when (residenceState) {
|
||||
is ApiResult.Idle, is ApiResult.Loading -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues),
|
||||
contentAlignment = Alignment.Center
|
||||
ApiResultHandler(
|
||||
state = residenceState,
|
||||
onRetry = {
|
||||
residenceViewModel.getResidence(residenceId) { result ->
|
||||
residenceState = result
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
errorTitle = "Failed to Load Property",
|
||||
loadingContent = {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
Text(
|
||||
text = "Loading residence...",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
CircularProgressIndicator()
|
||||
Text(
|
||||
text = "Loading residence...",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
is ApiResult.Error -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Error,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(64.dp),
|
||||
tint = MaterialTheme.colorScheme.error
|
||||
)
|
||||
Text(
|
||||
text = "Error: ${(residenceState as ApiResult.Error).message}",
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
Button(
|
||||
onClick = {
|
||||
residenceViewModel.getResidence(residenceId) { result ->
|
||||
residenceState = result
|
||||
}
|
||||
},
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
) {
|
||||
Text("Retry")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is ApiResult.Success -> {
|
||||
val residence = (residenceState as ApiResult.Success).data
|
||||
) { residence ->
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@@ -720,8 +690,6 @@ fun ResidenceDetailScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,50 +114,12 @@ fun ResidencesScreen(
|
||||
},
|
||||
floatingActionButtonPosition = FabPosition.End
|
||||
) { paddingValues ->
|
||||
when (myResidencesState) {
|
||||
is ApiResult.Idle, is ApiResult.Loading -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
}
|
||||
is ApiResult.Error -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Error,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(64.dp),
|
||||
tint = MaterialTheme.colorScheme.error
|
||||
)
|
||||
Text(
|
||||
text = "Error: ${(myResidencesState as ApiResult.Error).message}",
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
style = MaterialTheme.typography.bodyLarge
|
||||
)
|
||||
Button(
|
||||
onClick = { viewModel.loadMyResidences() },
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
) {
|
||||
Text("Retry")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is ApiResult.Success -> {
|
||||
val response = (myResidencesState as ApiResult.Success).data
|
||||
ApiResultHandler(
|
||||
state = myResidencesState,
|
||||
onRetry = { viewModel.loadMyResidences() },
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
errorTitle = "Failed to Load Properties"
|
||||
) { response ->
|
||||
if (response.residences.isEmpty()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@@ -436,8 +398,6 @@ fun ResidencesScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user